先说结论MA5上穿MA10是A股短线交易里最经典、也被验证过无数次的金叉信号但真正能把它“实时”跑起来、每天自动扫一遍全市场的代码远没有网上那些张口就来的版本那么简单。这篇我就把整套筛选逻辑、数据源选型、代码实现和踩坑记录完整拆开从零讲到一个可以直接丢到定时任务里用的版本。先说清楚这个东西是什么、能做什么所谓“实时筛选”本质是每根K线收盘后快速扫描全市场A股找出当天MA5从下方穿过MA10的个股。MA5是5日均线代表最近5天收盘价的平均值MA10是10日均线代表近10天的平均成本。MA5上穿MA10意味着短线资金开始以更高的价格持续入场把短周期成本推高到了中短周期成本之上这是短线趋势转强的技术确认。适合谁看想用Python做A股量化选股、又不想只停留在“看代码”阶段的人以及已经会用通达信、同花顺写公式选股但觉得内置筛选速度慢、没法自定义复杂逻辑的朋友。这篇博文会从均线金的本质、数据源对比、代码实现到常见报错排查一次性讲透保证你看完能直接落地一套自己的实时筛选脚本。1. 内容整体设计与思路拆解1.1 为什么MA5上穿MA10这个信号值得做实时筛选先说一个扎心的事实均线金叉这玩意儿几乎所有行情软件都能一键选出来通达信里一行公式就搞定。但如果你真的拿它做了一段时间实盘会发现两个问题——信号滞后和信号泛滥。滞后是因为均线本身就是滞后指标。MA5、MA10都是对历史价格的平滑股价先动、均线后动这是数学性质决定的改不了。但我们能做的是“实时”筛选也就是在信号刚形成的那一天、甚至在盘中接近形成时就把票找出来而不是等第二天复盘才看到那样黄花菜都凉了。泛滥是因为全市场5000多只股票哪怕只用MA5上穿MA10这一个条件每天符合的票可能也有几十上百只。这时候就需要叠加成交量、MACD、板块位置、是否ST、是否次新股等过滤条件。钝化过滤条件这件事就是纯代码的活也是这篇博文要重点展开的部分。先说清楚我的总体设计思路整个系统拆成四层——数据获取层、指标计算层、信号筛选层、结果输出层。数据获取层负责拉取全市场日线行情和实时行情指标计算层负责算MA5、MA10以及补充条件信号筛选层负责判断上穿并输出候选池结果输出层负责把结果写成CSV或推送到本地文件方便你每天打开看一眼。这个分层的核心目的是让每一层都能独立替换比如今天用A数据源明天想换B数据源只需要改第一层后面三层完全不用动。1.2 实时筛选和收盘后筛选的本质区别很多人对“实时”有误解以为盘中每秒扫一遍才叫实时。其实对于MA5上穿MA10这种日线级别的信号“实时”只需要做到“每天收盘后自动跑一遍”就够了。如果非要在盘中做反而会有个麻烦——盘中价格还在变动MA5和MA10的数值每时每刻都在变早盘的一根大阳线可能让MA5瞬间上穿MA10尾盘杀跌又跌回去一天之内信号反复横跳让你根本没法操作。所以我的建议是这套代码定位为“日线级实时筛选器”每天14:50或者收盘后15:05自动运行一次。14:50跑的好处是能赶在尾盘前看结果如果真想操作还有时间收盘后跑的好处是数据稳定绝对不会有盘中那种“一秒钟一个信号”的尴尬。实际使用中我推荐15:00收盘后跑然后把结果导出来晚上慢慢复盘第二天再决定是否介入这样心态会稳很多。既然定了收盘后跑那数据源必须满足两个条件一是当天数据完整二是拉取速度快。这直接决定了下面工具选型的走向。2. 工具选型与数据源对比2.1 三种常见方案的优劣对比做A股实时筛选市面上主流方案无非三种通达信等行情软件自带的公式选股、tushare pro、akshare。我分别聊一下实际用下来的感受。先说通达信自带选股。优点是真的快本地跑几秒钟能扫完整个市场而且数据准确、不用考虑积分和限流。缺点是逻辑表达能力有限复杂的过滤条件写起来很别扭比如我想在MA5上穿MA10的同时要求MACD在零轴上方且连续三天放量通达信的公式编辑器写这种条件会比较绕。另外通达信的实时选股需要一直开着软件适合手动操作不适合做成自动化定时任务。tushare pro是我最推荐的方案。它是目前国内量化圈用最多的数据接口日线数据、实时行情都有而且返回的是干净的DataFrame格式配合pandas做指标计算非常顺手。缺点是有积分门槛有些接口需要攒够一定积分才能调用但这属于一次性投入注册、签到、写点文档评论就能攒够不算门槛太高。akshare是完全免费开源的方案直接爬取公开网页数据不需要token。优点是零成本缺点是数据格式不稳定网站改版可能导致接口失效。我试过一段时间日线数据倒是没啥问题但实时分钟数据的字段会偶尔变动需要不定期维护。对于想跑一个长期稳定的定时任务我更倾向于tushare pro下面是两者的核心参数对比。对比项tushare proakshare是否需要token是需注册获取否直接安装调用日线数据稳定性高格式固定中偶尔受网站改版影响实时/分钟数据分钟接口需积分免费但字段变动风险高文档完善度较完善社区案例多一般文档略散适合场景长期稳定的自动化策略学习、临时拉取数据实际项目里我用tushare pro做主力数据源akshare作为备份。一旦tushare当天接口抽风可以临时切换到akshare顶上不至于让整个定时任务挂掉。2.2 tushare pro积分和接口权限的坑这里必须啰嗦一句因为十个人有八个会卡在这一步。tushare pro的很多接口都有积分门槛比如基础的daily日线接口需要120分实时分钟数据接口要更高的积分。新注册用户初始积分只有几十得靠签到、完善资料、写使用文档来攒。我建议的最低配置是先攒到120分能调用daily接口就够了。因为前面说了这套实时筛选本质是“日线级”只需要每天拉一次日线数据不需要分钟线。当然如果你对盘中筛选有执念那得继续攒分到2000以上才能开通分钟接口但那种场景通常已经属于高阶玩法不是这篇讨论的范围。注册之后记得设置自己的token代码里通过ts.set_token(你的token)配置然后优先用pro.daily()接口。这个接口一次最多拉6000条记录做全市场日线数据时要按股票代码循环拉取后面代码部分我会给出完整实现。3. 核心代码实现与实操拆解3.1 环境准备与依赖安装先说明运行环境Windows或Linux都行Python 3.8以上即可不需要装额外的数据库数据全部放内存里算跑完全市场也就几十秒。需要安装的库只有三个pandas、tushare、akshare。如果你只用tushare装前两个就够了。pip install pandas tushare # 可选作为备份数据源 pip install aksharepandas是必须的因为后面的MA计算、上穿判断全部靠它。tushare负责拉数据。akshare作为备选方案不装也不影响主流程。另外提醒一下Windows上如果装tushare遇到依赖问题优先升级pip再装我遇到过一次是numpy版本不兼容导致的升级pip、重新安装numpy就解决了。3.2 完整实现全市场MA5上穿MA10实时筛选直接上核心代码我从一个可以跑通的最小版本讲起再逐步叠加过滤条件。先定义一个获取单只股票日线数据的函数。tushare的daily接口传入股票代码和起止日期返回日期从新到旧排列的DataFrame字段包括trade_date、open、high、low、close、vol等。这里的核心坑是tushare返回的日期是字符串格式比如20250110必须转成pandas的datetime类型否则排序和切片都会出现问题。import pandas as pd import tushare as ts ts.set_token(你的token) pro ts.pro_api() def get_daily(code, start_date20241001, end_date20250110): df pro.daily(ts_codecode, start_datestart_date, end_dateend_date) if df is None or df.empty: return None df[trade_date] pd.to_datetime(df[trade_date], format%Y%m%d) df df.sort_values(trade_date).reset_index(dropTrue) return df然后定义均线上穿判断函数。这里有个关键逻辑判断“上穿”不是看今天的MA5比MA10大就行而是要求昨天MA5小于等于MA10、今天MA5大于MA10。这就是金叉的定义也是把“形成信号”和“维持上涨”区分开来的关键。def check_cross(df): if df is None or len(df) 20: return False df[ma5] df[close].rolling(5).mean() df[ma10] df[close].rolling(10).mean() # 用前一天的MA关系判断是否从下方向上穿过 prev_ma5 df[ma5].iloc[-2] prev_ma10 df[ma10].iloc[-2] curr_ma5 df[ma5].iloc[-1] curr_ma10 df[ma10].iloc[-1] if prev_ma5 prev_ma10 and curr_ma5 curr_ma10: return True return False然后是获取全市场股票列表。用stock_basic接口可以拿到所有上市股票的代码、名称、行业、上市日期等信息。这里顺便解决了一个关键问题过滤ST和次新股。ST股不碰这是做短线最起码的纪律上市不足60个交易日的次新股均线还没走稳MA5和MA10参考意义不大也要排除。def get_all_stocks(): stock_list pro.stock_basic(exchange, list_statusL, fieldsts_code,name,industry,list_date) # 过滤ST stock_list stock_list[~stock_list[name].str.contains(ST, naFalse)] # 过滤上市不足60个交易日的次新股 stock_list[list_date] pd.to_datetime(stock_list[list_date], format%Y%m%d) recent_cutoff pd.Timestamp.now() - pd.Timedelta(days120) stock_list stock_list[stock_list[list_date] recent_cutoff] return stock_list主程序部分遍历全市场拉数据、判断信号、收集结果。这里要控制一下节奏不要一次并发拉太多否则容易触发tushare的限流。实测下来用time.sleep(0.1)就够稳了全市场扫一遍大约需要8到10分钟放在定时任务里完全不叫事。import time def realtime_scan(): stocks get_all_stocks() results [] for _, row in stocks.iterrows(): code row[ts_code] try: df get_daily(code) if check_cross(df): # 补充今天的涨跌幅、收盘价方便复盘 latest_close df[close].iloc[-1] latest_pct df[pct_chg].iloc[-1] results.append({ code: code, name: row[name], industry: row[industry], close: latest_close, pct_chg: latest_pct }) except Exception as e: print(f处理{code}出错: {e}) time.sleep(0.1) result_df pd.DataFrame(results) result_df.to_csv(ma5_cross_ma10_results.csv, indexFalse, encodingutf-8-sig) print(f筛选完成共找到{len(result_df)}只股票) return result_df if __name__ __main__: realtime_scan()这个最小版本已经能跑通。但我要直接说结论这么裸跑出来的结果你实际用起来会很难受。因为全市场符合“单纯金叉”的股票太多了你需要叠加更多过滤条件把候选池压到每天几只到十几只的合理范围。3.3 叠加实战过滤条件从信号池到候选池我实际用的过滤条件有这么几个按重要性排序第一量能确认。金叉当天成交量必须高于最近5日均量。没有放量的金叉质量不高很可能是一日游行情。实现上就是拿当天的vol和过去5天vol的均值做对比要求vol mean(vol_5d) * 1.2即放量20%以上。第二MACD零轴位置。要求MACD的DIF线在零轴上方。MACD在零轴上方的金叉代表中期趋势已经走多这种金叉的成功率远高于零轴下方的反弹金叉。实现上可以直接用talib库也可以手写12日和26日EMA来算。手写其实很简单十几行代码的事我用的是手写版本免去额外装库的麻烦。第三市值过滤。流通市值低于30亿的票波动太大散户资金就能拉起来也容易被庄家控盘一般不建议做。高于300亿的大盘股MA5上穿MA10的弹性又不够涨也涨不了几个点。每个人风格不同我习惯盯30亿到200亿之间的中盘股。第四排除近两日已经大涨过的。如果金叉当天已经涨停或者涨幅超过7%第二天追进去的风险很大很容易高位站岗。我一般把当天涨幅上限设在5%到7%之间具体数值看你自己的风险偏好。把过滤条件整合进主程序的方式很简单在check_cross返回True之后再依次检查量能、MACD位置、市值全部通过才append到结果里。def advanced_check(df, row): if not check_cross(df): return False # 条件1当日放量 latest_vol df[vol].iloc[-1] avg_vol5 df[vol].iloc[-5:-1].mean() if latest_vol avg_vol5 * 1.2: return False # 条件2MACD快线在零轴上方 dif, dea, macd calc_macd(df) if dif.iloc[-1] 0: return False # 条件3当日涨幅不过高 if df[pct_chg].iloc[-1] 7: return False return True这里的calc_macd是一个手写的MACD计算函数按常规参数12、26、9来算。作为素材补充我贴一个简化实现有需要的可以直接拿去用def calc_macd(df, fast12, slow26, signal9): close df[close] ema_fast close.ewm(spanfast, adjustFalse).mean() ema_slow close.ewm(spanslow, adjustFalse).mean() dif ema_fast - ema_slow dea dif.ewm(spansignal, adjustFalse).mean() macd (dif - dea) * 2 return dif, dea, macd加完这些条件你再看一眼筛选结果会明显感觉到质量不一样。裸筛选可能有50只叠加后可能只剩10只而这10只就是第二天真正值得盯盘的票。3.4 定时任务让筛选自动化运行代码写完只是第一步真正的“实时”筛选必须让脚本在每天收盘后自动跑。Windows上用任务计划程序Linux上用crontab。Windows的做法打开任务计划程序新建任务触发器设为每天15:05操作设为运行Python脚本。这里有个坑任务计划程序默认的“起始于”目录不是脚本所在目录所以脚本里所有相对路径都可能找不到。解决办法很简单在脚本开头加两行把工作目录切换到脚本所在目录。import os, sys os.chdir(os.path.dirname(os.path.abspath(__file__)))Linux的crontab配置更简洁一行就够。5 15 * * 1-5 /usr/bin/python3 /home/user/stock_ma_cross.py注意这里限定周一至周五跑周末A股不开盘。另外日志输出建议重定向到文件里不然crontab的日志会丢出了问题没处查。我习惯这样写5 15 * * 1-5 /usr/bin/python3 /home/user/stock_ma_cross.py /home/user/stock_cron.log 21这样每次运行的结果和报错都会追加到log文件里第二天打开看log就知道昨晚跑没跑成功。4. 常见问题与排查技巧实录4.1 数据源报错与限流问题现象一提示抱歉您没有访问该接口的权限。这是tushare积分不够导致的。解决办法就是继续攒积分或者等积分够了下个交易日再跑。没有其他捷径。注意不要用多个账号绕过积分限制tushare的风控会检测到异常调用可能直接封号。现象二全市场扫描中途报错卡死。大概率是拉数据太频繁被限流了。我实测tushare的日线接口保持在每秒10次以内基本没事保险起见用了time.sleep(0.1)也就是每秒最多10只。如果报错特别频繁可以把sleep提高到了0.2。跑一次脚本多花几分钟而已不亏。现象三返回数据是空的。先检查是不是新股没有历史数据再检查日期范围是否正确。注意tushare的起止日期格式是字符串YYYYMMDD中间不能有横杠。这个格式问题我至少看到十个人踩过坑查半天报错才发现是格式不对。4.2 信号重复和漏选问题重复问题如果你把脚本放在盘中跑一只股票可能连续两天都被选中因为前天金叉、今天均线还没死叉但“上穿”条件已经不满足了。我的建议是严格使用“昨天小于等于、今天大于”的穿越定义而不是“今天大于”的状态定义。这一行差之毫厘结果天壤之别。漏选问题有些股票当天停牌没有当天数据。这时候df的最后一行其实是前一个交易日的数据程序会误判成当天金叉。解决办法是在get_daily时检查最新日期是否为最近交易日如果不是就跳过。这个逻辑加上之后整个筛选结果的准确率会提升一个档次。def is_latest_trade_date(df, expected_date): latest df[trade_date].iloc[-1] return latest expected_date在主程序里先获取今天的日期要排除周末在扫描前先判断一下最新交易日只处理那些最新数据为当日的股票。4.3 从代码到实盘一个盯着执行的提醒最后分享一个我个人的经验代码跑通了不代表策略就能赚钱。MA5上穿MA10这个信号它的本质是“跟随趋势”胜率是靠多次小止损少数大盈利积累出来的。实盘时我给自己定的纪律是第一金叉第二天如果高开超过3%不追如果平开或低开观察前30分钟能否翻红。第二单只股票仓位控制在总资金的10%以内因为即使过滤条件再严格单票胜率也就五五开。第三买入后第三天不涨就走不要把短线做成长线。均线金叉策略最忌讳的就是“被套就死扛”因为这是趋势跟踪策略不是价值投资策略逻辑搞混了纪律就容易崩。这套系统我从开始写到稳定运行前后改了三版。第一版瞎跑信号几百个第二版加了量能和MACD过滤日子好过多了第三版加入自动定时和停牌检查才算真正“实时”起来。现在每天收盘后我就不用管了早上起来扫一眼结果文件心里大概有数今天该盯哪几只。这就是“实时筛选”真正的价值——不是替你决策而是把5000只股票缩减成5只让你把精力花在真正值得研究的对象上。