
简介这是一份以股市分析为主题的 Jupyter Notebook 数据科学项目包适合对量化投资、金融数据分析感兴趣的开发者与数据科学学习者。项目整体采用标准的数据科学工程组织方式将数据、特征工程、模型与可视化分层并重点提供三套分析流程超级投资者买入标的解析、标普500持有模拟以及EDA探索性分析覆盖从数据清洗、变量探索到持仓模拟的常见环节能够帮助读者快速建立一套可复用的分析框架。压缩包共包含34个文件大小约1.75MB以Python脚本、Notebook、RST文档和Makefile配置文件为主其中14个py文件承担数据处理、模型构建与工具函数等职责3个ipynb文件承载核心分析演示Makefile与tox.ini便于自动化运行、环境配置与测试。目前已有469人学习下载使用者可直接参照目录约定和Notebook流程复现分析或替换为自己的数据源扩展个股与指数研究是接触股市数据项目的轻量级参考。 如果只是想看股票涨跌打开行情软件就够了。但如果你和我一样想要搞清楚一只股票为什么涨、凭什么跌、背后那些技术指标到底在说什么就会意识到手动翻页面太浪费时间。于是我动手写了一套名为stock_analysis的股市分析工具把行情数据拉下来用代码批量计算指标再根据结果过滤候选标的。这套东西不算复杂但确实解决了不少实际问题今天就把完整思路和踩坑过程分享一下。很多人觉得股市分析就是装个软件看K线或者听专家讲宏观。但真正落到自己操作上你会发现核心痛点不是看不看得到行情而是信息太多看不过来。当时我手上有一批自选股每天收盘后想快速知道哪些票放量、哪些票突破均线、哪些票的MACD出现金叉如果一个个打开软件去点至少要花一个多小时。而用 stock_analysis 之后全市场扫描一遍只需要几分钟结果还能按自定义规则排序。这篇文章就是写给那些想用代码辅助判断、又不想被各种神通广大的神棍理论带偏的朋友。1. 从看盘到写码我决定自己做分析工具的三个理由1.1 现成软件满足不了批量对比需求市面上的行情软件功能确实强大但它们都是给人眼设计的。你可以在一个屏幕上看四五只股票的走势图但很难同时对比几百只股票的关键指标。而股票分析的工作流里很多决策恰恰需要横向对比今天哪些票的成交量放大到昨天的两倍哪些票的短期均线刚刚上穿长期均线哪些票的布林带收窄到了极点这些筛选需求用软件自带的选股器也能做但规则一旦复杂起来比如20日均线大于60日均线同时当日成交量大于5日均量1.5倍并且RSI介于50到70之间很多免费软件的表达式就写得让人头疼。写代码的好处是能把所有规则定义得清清楚楚逻辑一目了然。之前我试图用某软件的自带公式编辑器实现类似功能折腾了整整一个晚上最后被函数嵌套层数限制搞得心态崩溃。换到 Python 之后同样的逻辑二十行代码搞定而且每次跑批还能自动保存结果。1.2 数据口径不统一亲手摸过才放心第二个原因是数据源之间的差异。那时候我常用两个行情软件对比同一只股票的均线位置发现有时候金叉、有时候没金叉一度以为是自己眼花了。后来仔细核对才发现有些平台把除权日的数据做复权处理有些则直接切断了历史价格导致均线算法结果完全不一样。自己做 stock_analysis 之后我从一开始就明确所有计算都基于前复权价格。理由很简单前复权能保证当下价格与近期历史价格连续可比对于技术指标计算更贴近实际交易视角。这些细节用现成软件时根本不会注意但真正自己实现一遍才发现数据源的一致性比策略本身还重要。1.3 想验证某套逻辑到底行不行需要可复现的代码市场上流传着无数种选股逻辑比如MACD金叉买入放量突破平台买入缩量回调到20日线买入等等。很多人凭感觉觉得某条逻辑有效但从来不去验证。我当时的想法很直接能不能写一套程序把过去三年的历史数据跑一遍模拟每条逻辑的收益曲线这个需求只有自己写代码才能实现因为逻辑本身必须精确到哪一天、哪个价格、什么条件触发操作没法用嘴说。2. 数据清洗与指标计算拿到的数据不能直接用2.1 数据源怎么选免费接口的取舍我在 stock_analysis 里用的是公开的免费行情数据接口这当然存在一些限制比如单日请求次数有限、部分字段缺失但胜在零成本、够用。对个人研究来说免费接口已经能满足绝大多数需求。如果你有更稳定的数据源那更好但注意要保持数据格式统一否则后面所有指标计算都要跟着改。实际获取的数据里最常见的脏数据是停牌日某天成交量、成交额全是0但价格保持前一天收盘价。许多现成软件会直接跳过停牌日但如果你机械地拿去算均线结果就会把停牌日当成正常交易日重复计入均线产生偏差。所以清洗的第一步就是把停牌日识别出来并做标记计算指标时单独处理。2.2 复权计算跌坑最深的环节复权是一个绕不开的大坑。股票分红送股后价格会出现跳空如果不复权技术指标会瞬间失效。我一开始偷懒直接用原始价格算均线结果遇到除权日5日均线直接从8元跳到7元看起来像暴跌其实是分红导致的。后来我彻底改用前复权价格。实现前复权的方式有很多有些接口自带有些需要自己计算。我的经验是直接利用数据接口提供的复权因子把历史价格乘以复权因子得到前复权价格。需要注意前复权价格是动态变化的每出现一次新的除权除息历史价格都会被重新调整。所以不要把所有数据永久缓存最好每周重新拉取一次历史数据保证复权因子是最新的。2.3 自定义指标类把每个指标写成独立函数为了后续扩展方便我并没有把指标计算糊在一个大函数里而是每个指标单独写一个方法输入是干净的历史行情数据输出是一个和原始数据对齐的序列。这样做的好处是新增一个指标只需要增加一个方法完全不影响原有代码。代码结构大致长这样class Indicators: def __init__(self, df): self.df df # 包含 date, open, high, low, close, volume def sma(self, n): return self.df[close].rolling(windown).mean() def ema(self, n): return self.df[close].ewm(spann, adjustFalse).mean() def macd(self, fast12, slow26, signal9): dif self.ema(fast) - self.ema(slow) dea dif.ewm(spansignal, adjustFalse).mean() return dif, dea, (dif - dea) * 2 def rsi(self, n14): diff self.df[close].diff() up diff.clip(lower0).rolling(windown).mean() down -diff.clip(upper0).rolling(windown).mean() rs up / down.replace(0, float(nan)) return 100 - 100 / (1 rs)这个架构看起来平平无奇但妙在每个函数都只依赖 DataFrame 的结构不依赖任何全局状态。这样单测也容易写比如我测试过 MACD 在连续上涨行情下 dif 始终大于 dea结果确实如此。3. 核心选股逻辑从行情密度到趋势强度3.1 技术指标不是越多越好关键是组合逻辑刚开始接触股票分析时我也曾陷入指标收集癖把 BOLL、KDJ、CCI、OBV 全都塞进代码里每个指标都看结果哪个都不敢信。后来逐步淘汰最终在 stock_analysis 里保留的主力选股逻辑只有三个维度趋势、动量和成交量。趋势维度看均线排列和价格相对均线的位置动量维度看 MACD 的金叉死叉状态和加速度成交量维度看量比和量价关系。为什么选这三个维度因为趋势决定了方向动量决定了力度成交量决定了资金关注度三者组合起来至少能过滤掉大部分跟风交易。举个例子我经常用的一个简单筛选逻辑是20日均线大于60日均线、MACD 的 DIF 值大于 DEA 值、今日成交量大于5日均量。就这三条规则就能把全市场大部分阴跌或者横盘的股票过滤掉。当然它也会漏掉一些大涨前的无量标的这无法避免任何规则都有取舍。3.2 用连续N天满足条件替代单点信号早期版本中我只要看到当天某个信号触发就输出一个候选结果。后来回测发现很多单点信号是噪音。比如 MACD 金叉在震荡行情里会反复金叉死叉如果当天触发就记录很容易陷入反复打脸。后来我把逻辑改成条件连续满足N天才算有效。比如 not just MACD 金叉当天而是 DIF 和 DEA 的差值连续3天放大才认为动量在持续。这样一来候选数量大幅减少但质量明显提升。这在代码实现上只是一行条件判断的事但逻辑本质从抓取瞬间信号变成了确认持续状态效果差别非常大。3.3 板块联动不只看个股还要看同伴我还在 stock_analysis 里加了一个比较朴素的分析维度把同行业股票放在一起看如果多只股票同时出现放量突破说明这个板块可能正在被资金关注。这个功能最初实现起来很麻烦因为需要行业分类数据后来我直接从公开数据源里抓取了行业板块列表然后对每个板块做达标家数统计。实现思路不复杂就是遍历板块中的每只股票运行选股规则统计达标数量。如果某板块里超过三成成分股同时满足趋势动量量能条件就标记为强势板块再重点看板块中的龙头。这种分析方式比单纯看个股指标更全面也让我在复盘时能更快抓住主线。4. 回测与实盘的落差我踩过的几个关键坑4.1 未来函数回测收益虚高的头号元凶写了回测代码之后第一版结果出来吓了我一跳按策略跑过去三年年化收益率高得离谱。冷静下来一查发现是未来函数在作祟。当时计算 MACD 信号时我在第 T 天收盘时用了收盘价格但判断条件里又引用了下一个交易日的数据。简单说就是提前知道了明天的结果回测自然完美。排查未来函数有个土办法打印出信号触发日期和第二天价格肉眼看有没有明显的不合理。更规范的做法是把所有指标计算严格限制在截至当日收盘可得数据范围内。我花了一天时间重构代码把所有计算都放进一个循环里保证每天只用到过去的数据改完之后的收益曲线变得真实可信虽然没那么惊艳但至少不会自欺欺人。4.2 手续费和滑点不加这两项就是耍流氓另一个让回测失真的因素是交易成本。刚开始我自己写的回测模型里买入价直接用第二天开盘价卖出也直接用当天收盘价完全没考虑手续费和滑点。结果实盘跑起来之后策略的收益明显比回测差一截因为每次交易都有佣金和印花税支出。后来我给回测加了固定成本模型买入时按成交金额加收万分之二点五佣金卖出时加收万二点五佣金外加千一的印花税同时每次交易额外计提千一的滑点成本。这一调整把年化收益直接打掉了好几个点但也让回测结果更接近真实。我建议任何回测都别省这一步哪怕你用的模拟盘没有真实手续费也请务必在代码里加上成本参数否则策略评估完全失真。4.3 接口限流与数据缺失实盘跑批的隐藏炸弹当我把 stock_analysis 从回测切换到每日实盘扫描时发现原来代码不一定扛得住。免费接口对调用频率有严格限制一开始我全市场扫描时每分钟请求几百次直接被封禁导致当天差点没跑出结果。后来我调整为分批拉取每只股票之间加 sleep并且做了请求失败自动重试才稳定下来。另外新股上市初期数据不足没法计算20日均线如果不做特殊处理程序就会报 KeyError 或者返回 NaN。我的处理方式是计算指标时检查数据量是否满足所需窗口如果不够就直接剔除不参与候选。这个逻辑听起来很简单但如果不加判断程序跑到某只次新股时会直接崩溃整个扫描流程都会中断。5. stock_analysis 的模块划分与后续扩展思路5.1 现在的代码结构数据层、指标层、策略层三件套目前这套工具不算大但模块边界很清晰。数据层负责从接口拉取数据并清洗指标层负责计算各类技术指标并缓存计算结果策略层负责把指标组合成筛选规则并输出结果。三层之间只通过标准化的 DataFrame 传递数据互不干扰。这种结构最大的好处是想加一个新策略不需要改数据层和指标层只要在策略层写一个新函数把已有的指标当输入用就行。我现在的策略层已经有五六个不同规则的筛选器分别针对中长线趋势、短线放量、超跌反弹等场景每个筛选器输出一批股票代码再汇总到一个表格里方便对比。5.2 后续可以加的方向基本面因子和情绪面因子纯技术指标的局限性很明显它只反映价格和量的历史规律无法衡量一家公司的实际盈利能力。我接下来的计划是在 stock_analysis 里加入基本面因子比如市盈率、市净率、毛利率和净利润增速把这些数据也拉下来作为第二层过滤器。情绪面因子则更难量化但可以从换手率、涨跌停家数、连续涨停天数等数据间接推测。虽然不能直接给出情绪温度计这种玄学结论但至少能看到市场活跃度是否有极端异动帮我在执行策略时更谨慎地控制仓位。5.3 建议不要追求大而全先解决自己最痛的环节很多刚接触股市分析的人一开始就幻想着做一个全自动决策系统输入一个代码输出买卖信号还能自动交易。我觉得这个目标太大而且也没有必要。我的建议是先明确自己最痛的点比如每天复盘太费时不知道如何从几百只股票里快速找候选然后针对这个点写代码跑通之后再逐步扩展。stock_analysis 这个项目到今天也没有实现自动下单因为我始终觉得交易执行层面的东西更需要谨慎涉及资金安全和稳定性不是靠个人兴趣项目就能搞定的。作为分析辅助工具它能帮我省下大量的复盘时间让我把精力更多放在研究逻辑和风险控制上这已经足够有价值了。最后分享一个我在实际使用过程中摸索出的小技巧给扫描结果加一列信号首次出现日期而不是只显示今天的信号。这样一来如果某只股票的信号是今天刚出现的属于首次启动可以重点关注如果已经连续出现5天可能已经涨幅不小追高就要谨慎。这个改动只花了几行代码但对决策时的感觉帮助很大。说白了代码是做减法的把这个工具当作一个纪律化的筛选助手往往比东看一眼西看一眼靠谱得多。本文还有配套的精品资源点击获取