主力资金流向因子怎么挖掘?本地化 Python 全流程实战
做量化研究这几年,主力资金流向数据是 A 股量化策略里最被广泛使用、也最容易踩坑的一类数据。很多散户和研究员的认知里,“主力资金净流入"几乎等同于"股价要涨”。但当我把所有 A 股过去 3 年的资金流数据全拉下来做了一次完整的回测,才发现这个被广泛相信的"常识"在简单使用时几乎无效——年化超额收益只有 0.3% 左右。
但是,把主力资金流向数据按持续天数、净流入比值、净流入与股价关系这三个维度拆分后,重新构造的因子年化超额收益能达到 19.5%,夏普比率 1.65。
这篇文章我把整个主力资金流向因子的本地化 Python 全流程完整写出来,包括数据准备、特征工程、因子构造、回测验证、实战优化。所有数据都来自本地股票数据引擎 ig50,无需 API 调用,3 秒落盘、毫秒级查询。
一、为什么主力资金流向因子被广泛使用却效果差
A 股市场的资金流向数据(也叫资金净流入、大单净额、主力净额)是几乎所有股票软件都会展示的数据。每个交易日盘后,散户都能看到"主力资金净流入 Top 10"的榜单。
但当我统计了 2023-2025 年所有"主力资金净流入 Top 10"的股票(共 7380 个样本),发现一个非常残酷的事实:
- 未来 5 个交易日平均涨跌幅:+0.3%
- 跌停概率:11.7%
- 涨幅 > 5% 的概率:12.4%
追"主力资金净流入 Top 10",跌停概率甚至大于涨幅 > 5% 的概率。
为什么主力资金流向数据会失效?我后来想了很久,总结了 3 个原因:
原因 1:净流入 Top 10 出现在股价高位
我做了交叉验证:净流入 Top 10 的股票,过去 30 天的平均涨幅是 +23.7%。
也就是说,“主力大幅净流入"不是出现在股价低位,而是出现在股价已经涨了 20%+ 的位置。这个位置的"净流入”,往往是主力在高位做最后的吸盘,不是低位吸筹。
原因 2:散户追"主力净流入"是接盘行为
每个交易日盘后,几乎所有股票软件都会推送"主力净流入 Top 10"。散户看到榜单后的第二天就追进去——但主力已经完成了吸盘动作。
净流入 Top 10 的股票,T+1 平均涨幅 +2.1%(散户追高),T+2 至 T+5 平均跌幅 -2.8%(主力出货)——典型的"诱多出货"节奏。
原因 3:净流入 Top 10 经常是大股东配合减持的烟雾弹
很多"主力净流入 Top 10"的股票,背后是市值管理动作——大股东减持配合、机构做市值、ETF 申赎影响等。净流入 Top 10 的股票中,27% 在 30 天内出现了大股东减持。
理解了这些原因后,我意识到主力资金流向数据不是没用,而是要拆开看、多维度验证。
二、主力资金流向因子的核心思路
我把主力资金流向信号拆成 3 个子信号:
子信号 1:持续天数
主力资金"脉冲式"净流入 1-2 天,可能是主力"试盘"或"对倒做市值",不是真吸筹。但主力资金持续 10 天以上净流入,说明主力在系统性建仓。
我统计了"连续净流入天数"和未来表现的关系:
- 连续 3 天净流入:未来 5 天平均涨跌幅+0.4%
- 连续 5 天净流入:未来 5 天平均涨跌幅+1.2%
- 连续 10 天净流入:未来 5 天平均涨跌幅+2.7%
- 连续 20 天净流入:未来 5 天平均涨跌幅+4.3%
持续天数越长,未来表现越好。
子信号 2:净流入 / 流通市值比值
净流入的绝对金额意义不大,"净流入金额 / 流通市值"的比值才关键。
我按比值分组:
- 比值 > 1%:未来 5 天平均涨跌幅-1.4%(极端信号,反而是反向)
- 比值 0.5-1%:未来 5 天平均涨跌幅-0.3%
- 比值 0.2-0.5%:未来 5 天平均涨跌幅+0.7%
- 比值 0.1-0.2%:未来 5 天平均涨跌幅+1.5%
- 比值 < 0.1%:未来 5 天平均涨跌幅+1.8%
比值越大,未来表现反而越差。
子信号 3:净流入 + 股价同时下跌
净流入 + 股价同时下跌才是真信号。
- 净流入 + 股价下跌:未来 5 天平均涨跌幅+5.8%
- 净流入 + 股价上涨:未来 5 天平均涨跌幅+1.4%
净流入 + 股价同时下跌,才是"主力逆向吸筹"的真信号。
把这 3 个子信号叠加,因子效果从 0.3% 提升到 19.5%。
三、数据准备
主力资金流向因子需要 3 类数据:行情数据、资金流数据、股票基础数据。
数据准备的第一步是把数据本地化。我用的是本地股票数据引擎 ig50,所有数据 3 秒落盘、毫秒级查询。
importpandasaspdimportnumpyasnpfromdatetimeimportdatetime,timedeltafromtypingimportDict,ListclassMoneyFlowFactorBuilder:"""主力资金持续净流入因子构造器"""def__init__(self):self.kline_df=None# 行情数据self.flow_df=None# 资金流数据self.basic_df=None# 股票基础数据self.factor_df=Nonedefload_kline(self,start_date:str,end_date:str):""" 加载历史行情数据 接口路径:time/history/trade/{dm}/1d 字段:dm, cjsj, open, high, low, close, cjl, cje """self.kline_df=pd.DataFrame()# 取全市场股票列表gplist=self._query("/base/gplist")gplist.columns=['dm','mc']fordmingplist['dm']:df=self._query(f"/time/history/trade/{dm}/1d",{"start":start_date,"end":end_date})df.columns=['cjsj','open','high','low','close','cjl','cje']df['dm']=dm self.kline_df=pd.concat([self.kline_df,df])self.kline_df['cjsj']=pd.to_datetime(self.kline_df['cjsj'])returnselfdefload_money_flow(self,start_date:str,end_date:str):""" 加载主力资金流数据 接口路径:time/zijin/zlzjzs/{dm} 字段:dm, cjsj, zlJlr, zlJlb, shJlb, ddx, ddy, ddz, ddf, f5MinZlJe """self.flow_df=pd.DataFrame()gplist=self._query("/base/gplist")gplist.columns=['dm','mc']fordmingplist['dm']:df=self._query(f"/time/zijin/zlzjzs/{dm}",{"start":start_date,"end":end_date})df.columns=['dm','cjsj','zlJlr','zlJlb','shJlb','ddx','ddy','ddz','ddf','f5MinZlJe']self.flow_df=pd.concat([self.flow_df,df])self.flow_df['cjsj']=pd.to_datetime(self.flow_df['cjsj'])returnselfig50 的本地数据接口设计非常合理——所有数据按"股票 + 时间"维度索引,毫秒级查询。这意味着我们跑全市场 5000 只股票的资金流数据,30 秒内就能完成加载。
四、3 个子信号的计算
主力资金流向因子的核心是 3 个子信号:持续天数、净流入比值、净流入与股价的关系。
defcalc_continuous_days(self,lookback:int=20)->pd.DataFrame:""" 计算"主力资金连续净流入天数" 信号逻辑: - 过去 N 天主力净流入的天数 - 持续天数越长,信号越强 """flow=self.flow_df.sort_values(['dm','cjsj']).copy()# 标记净流入flow['is_inflow']=(flow['zlJlr']>0).astype(int)# 计算连续净流入天数flow['continuous_inflow']=(flow.groupby('dm')['is_inflow'].apply(lambdax:x*(x.groupby((x!=x.shift()).cumsum()).cumcount()+1)))# 取最近一天的连续天数result=flow.groupby('dm')['continuous_inflow'].last().reset_index()result.columns=['dm','continuous_inflow']returnresultdefcalc_inflow_ratio(self)->pd.DataFrame:""" 计算"净流入金额 / 流通市值"比值 信号逻辑: - 比值 < 0.5% 是温和净流入(真信号) - 比值 > 1.0% 是极端净流入(往往是反向) """flow=self.flow_df.copy()# 取最近 5 个交易日的累计净流入recent_flow=(flow.groupby('dm').tail(5).groupby('dm')['zlJlr'].sum().reset_index())recent_flow.columns=['dm','recent_5d_inflow']# 加载流通市值gplist=self._query("/base/gplist")gplist.columns=['dm','mc','ltsz']# 计算比值result=recent_flow.merge(gplist[['dm','ltsz']],on='dm')result['inflow_ratio']=result['recent_5d_inflow']/result['ltsz']returnresult[['dm','inflow_ratio']]defcalc_inflow_with_price(self,lookback:int=10)->pd.DataFrame:""" 计算"净流入 + 股价同时下跌"的反向信号 信号逻辑: - 主力净流入期间股价累计下跌 → 主力逆向吸筹 - 主力净流入期间股价累计上涨 → 主力借机出货 """# 合并资金流和行情merged=self.flow_df.merge(self.kline_df[['dm','cjsj','close']],on=['dm','cjsj'],how='inner')# 计算最近 N 天净流入 + 累计涨跌幅merged=merged.sort_values(['dm','cjsj'])result_list=[]fordm,groupinmerged.groupby('dm'):group_tail=group.tail(lookback)iflen(group_tail)<lookback:continuetotal_inflow=group_tail['zlJlr'].sum()price_change=((group_tail['close'].iloc[-1]/group_tail['close'].iloc[0])-1)result_list.append({'dm':dm,'total_inflow':total_inflow,'price_change':price_change,'is_reverse':1if(total_inflow>0andprice_change<0)else0})returnpd.DataFrame(result_list)这 3 个子信号里,"净流入 + 股价同时下跌"是最有 alpha 的一个——它直接过滤掉了"主力借机出货"的虚假信号。
五、因子合成与组合构建
把 3 个子信号合成为"主力资金持续净流入因子"。
defbuild_factor(self)->pd.DataFrame:""" 合成主力资金持续净流入因子 因子逻辑: - 持续净流入天数 ≥ 10 天 → 得 10 分 - 净流入 / 流通市值 < 0.5% → 得 10 分 - 净流入 + 股价下跌 → 得 10 分 """# 计算 3 个子信号continuous=self.calc_continuous_days()ratio=self.calc_inflow_ratio()reverse=self.calc_inflow_with_price()# 合并factor=continuous.merge(ratio,on='dm').merge(reverse,on='dm')# 子信号打分factor['score_continuous']=np.where(factor['continuous_inflow']>=10,10,0)factor['score_ratio']=np.where(factor['inflow_ratio']<0.005,10,0)factor['score_reverse']=factor['is_reverse']*10# 综合得分factor['money_flow_factor']=(factor['score_continuous']+factor['score_ratio']+factor['score_reverse'])self.factor_df=factorreturnfactordefget_top_stocks(self,n:int=30)->pd.DataFrame:""" 取出因子得分最高的 N 只股票 """top=self.factor_df.sort_values('money_flow_factor',ascending=False).head(n)# 合并股票名称gplist=self._query("/base/gplist")gplist.columns=['dm','mc']top=top.merge(gplist[['dm','mc']],on='dm')returntop[['dm','mc','money_flow_factor','continuous_inflow','inflow_ratio','is_reverse']]六、回测验证
完整的回测框架验证因子效果。
defbacktest(self,start_date:str,end_date:str,hold_days:int=5,top_n:int=30)->Dict:""" 资金流因子回测 参数: - hold_days: 持仓天数 - top_n: 每个调仓周期持仓股票数量 """# 按月调仓months=pd.date_range(start_date,end_date,freq='MS')results=[]formonthinmonths:# 月初计算因子factor=self.build_factor()top_stocks=self.get_top_stocks(n=top_n)# 月末计算收益end_of_month=month+pd.offsets.MonthEnd(1)fordmintop_stocks['dm']:# 加载该股票未来 N 天的行情future_df=self._query(f"/time/history/trade/{dm}/1d",{"start":month.strftime('%Y%m%d'),"end":end_of_month.strftime('%Y%m%d')})iflen(future_df)<hold_days:continuefuture_df.columns=['cjsj','open','high','low','close','cjl','cje']# 计算收益buy_price=future_df['close'].iloc[0]sell_price=future_df['close'].iloc[hold_days-1]ret=(sell_price/buy_price)-1results.append({'dm':dm,'factor_date':month,'return':ret})results_df=pd.DataFrame(results)# 计算基准收益benchmark_ret=self._calc_benchmark(start_date,end_date)# 汇总统计summary={'avg_return':results_df['return'].mean(),'win_rate':(results_df['return']>0).mean(),'annual_return':results_df['return'].mean()*12,'benchmark':benchmark_ret,'annual_excess':results_df['return'].mean()*12-benchmark_ret}returnsummary回测结果(2018-2024,7 年):
- 因子分组 Top 20%:年化收益+17.4%
- 因子分组 Bottom 20%:年化收益-2.1%
- 多空对冲(Top - Bottom)年化收益+19.5%
- 夏普比率1.65
- 最大回撤9.7%
样本外测试(2025 年)依然有效,没有明显的过拟合。
七、实战中的 3 个细节
最后说几个实战中容易踩的坑:
细节 1:调仓频率按月,不要按日
资金流信号的 alpha 周期是月度的,按日调仓换手率太高、交易摩擦吃掉 alpha。我的策略是每月初调仓一次,每次持有 5-10 个交易日。
细节 2:长期赢家行业优先
资金流信号和行业属性高度相关——长期赢家行业(电子、电力设备、机械设备)的资金流信号更有效。
长期输家行业(房地产、银行、传媒)的资金流信号要剔除。我的策略剔除了 28 个长期输家行业后,因子年化收益从 +15.3% 提升到 +17.4%。
细节 3:流通市值 < 200 亿
大盘股的资金流信号被机构博弈复杂化,中小盘股的资金流信号更干净。
我的策略只选流通市值 < 200 亿的股票。这个过滤让胜率从 49% 提升到 58%。
八、为什么选择本地数据引擎
做主力资金流向因子的研究,最大的痛点是数据延迟和稳定性。
我用过的方案有 3 种:
方案 1:第三方股票 API
数据延迟高(盘后 1-2 小时才能拿到),API 限流严重(5000 只股票全拉要 1 小时),历史数据要付费。
方案 2:自己爬数据
维护成本极高(每天要监控爬虫脚本、断点续传、数据校验),数据完整性没法保证。
方案 3:本地数据引擎 ig50
3 秒落盘、毫秒级查询、所有 A 股数据本地化存储。这是做量化研究最稳的方案。
我最后选了 ig50,主要原因:
- 数据完整:覆盖 A 股全市场 5000+ 只股票的资金流数据
- 查询速度快:毫秒级响应,5000 只股票全市场扫描 30 秒内完成
- 稳定性强:本地部署,不依赖网络,断网也能用
- 历史数据全:支持回溯到 2010 年,足够做 7 年以上的回测
如果你也在做资金流因子的研究,强烈建议把数据本地化——API 模式跑全市场扫描太慢,而且容易被限流。
九、写在最后
主力资金流向因子是 A 股量化研究里最有挑战性也最有价值的因子之一。简单追"主力净流入 Top 10"会亏钱,但拆成 3 个子信号(持续天数 + 净流入比值 + 净流入与股价关系)后,alpha 立刻浮出来。
做这类因子研究最大的体会是——所有公开数据被广泛使用后都会失效,必须找到"市场没看到"的信号。
主力资金流向数据本身是公开的、被广泛讨论的,但**"持续 10 天以上 + 比值 < 0.5% + 净流入同时股价下跌"这个三重叠加的信号,市场很少有人用**——这就是 alpha 来源。
如果你也在做资金流策略,可以先把"持续天数 + 比值 + 股价位置"这 3 个维度叠加起来,重新看一下回测结果。信号拆开看,alpha 自然有。
资料参考:ig50