daily_stock_analysis 这个名字说得很直白:每天拉一次股票行情、算几个常用技术指标、按规则筛选,再把结果整理成能看的报告。ZhuLinsen/daily_stock_analysis 这类仓库在 GitHub 上很常见,核心就是把数据获取、指标计算、条件筛选、报表输出串成一条自动化链路。如果你正在找 Python 数据分析或定时任务练手项目,它比单纯的“爬虫拉数据”多一层分析逻辑,比完整量化回测框架又轻很多,正好卡在中间。
我建议先别急着 clone 下来就跑,先想清楚它到底解决什么问题:很多人手里有行情数据,也有 pandas 基础,但每天开盘前或收盘后都要重复做同样的事——拉数据、过滤无效股票、算指标、挑出符合条件的标的。daily_stock_analysis 就是把这段重复过程脚本化。下面按实际落地顺序拆开讲,涉及数据链路、指标计算、筛选输出、定时调度和常见坑。
先提醒一件事:这类工具最大的价值是让重复工作自动化,不是“算出一个能稳赚的信号”。技术指标本质上是对历史价格和成交量的数学描述,不能保证预测未来。用工程化心态看它,收获比单纯盯结果大得多。
1. 先想清楚 daily_stock_analysis 的数据链路在做什么
1.1 核心字段和处理对象
任何日常股票分析脚本,核心输入都是日线行情数据。最常见的一组字段包括:
| 字段 | 含义 | 常见用途 |
|---|---|---|
| date | 交易日期 | 排序、按日期切片 |
| code | 股票代码 | 区分标的 |
| name | 股票名称 | 可读性、筛选 ST |
| open / close | 开盘价 / 收盘价 | 计算涨跌幅、K 线 |
| high / low | 最高价 / 最低价 | 波动范围、ATR 等指标 |
| volume | 成交量 | 量能判断 |
| amount | 成交额 | 过滤流动性差的股票 |
| turnover | 换手率 | 活跃度判断 |
第一步不是写代码,而是确认数据源能不能稳定返回这些字段。很多接口字段名不一致,有的叫vol,有的叫volume,有的成交量单位是手,有的是股。正确做法是先把返回数据打印一条样本,确认字段、类型和单位,再开始存库和分析。这个步骤省掉之后,后面会在数据清洗上反复返工。
1.2 数据范围决定项目复杂度
分析范围通常有三种选择:
- 只分析自选股或指数:代码量最少,适合新手,跑一遍只要几秒。
- 分析某个板块或指数成分股:数据量适中,可以做横向对比。
- 分析全市场 A 股:最完整,但数据量大、接口调用频繁、运行时间长,还要处理停牌、新股、退市等特殊情况。
我建议从第一种开始。先用三五只股票把整个链路跑通,再逐步扩大范围。一上来就拉全市场,报错时你很难判断是数据源问题、指标问题,还是脚本逻辑问题。把变量控制住,排错成本会低很多。
1.3 数据源和存储方式怎么选
国内常见的免费数据源有 akshare、tushare、baostock 等,各有特点。选型时主要看三件事:
- 是否需要注册和 token;
- 单次能返回多少条数据、有没有频率限制;
- 返回的数据质量如何,需不需要额外清洗。
无论用哪个数据源,我都会先把原始数据存到本地再分析。免费接口稳定性通常一般,每天重复请求相同的历史数据既慢又容易被限流。落盘之后,每天只需要增量拉取最新交易日的数据,分析时直接从本地读取,速度会快很多。
存储方面,小规模用 CSV 或 SQLite 就够了,没必要为这个项目专门上 MySQL。
注意:数据源接口经常升级,字段名和返回结构可能变化。把数据获取单独封装成一个模块,以后迁移数据源时只需要改一个文件。
2. 环境、依赖和目录结构,跑之前先定好
2.1 运行环境与依赖
这类项目用 Python 最合适,生态里数据分析、定时任务、图表输出都很成熟。常见环境是 Python 3.9 及以上版本,配合这几个库:
- pandas:处理行情表格;
- requests:调用数据源 HTTP 接口;
- schedule 或 APScheduler:定时触发任务;
- matplotlib:画 K 线和指标图;
- openpyxl:导出 Excel。
如果你在 Linux 服务器上跑,需要注意中文字体问题,画图时中文会变成方块。提前安装 wqy-zenhei 这类字体,或者在 matplotlib 里指定中文字体路径。这个问题在 Windows 上不明显,一上 Linux 就立刻暴露。
2.2 目录结构可以参考这个
不用一开始搞得很复杂,但要有区分,否则跑几天之后报表、日志、临时数据混在一起,很难维护。
daily_stock_analysis/ ├── config.py # 配置:数据源、股票池、规则参数 ├── fetcher.py # 数据获取模块 ├── indicators.py # 技术指标计算模块 ├── screener.py # 筛选规则模块 ├── reporter.py # 报表输出模块 ├── scheduler.py # 定时调度入口 ├── data/ │ ├── raw/ # 原始行情 │ └── processed/ # 清洗后的数据 ├── reports/ # 每日输出 └── logs/ # 运行日志这个结构没有高深的地方,核心是让每个环节可以单独测试。fetcher 可以单独跑,indicators 可以单独验证,screener 可以只针对本地数据运行。模块之间通过 DataFrame 传递,接口保持简单。
2.3 配置和参数单独放
股票池、指标参数、筛选条件、输出路径不要写死在代码里。放一个 config.py 或 config.yaml,每次调整只改配置。比如:
# config.py 示例,实际参数按你的环境调整 STOCK_POOL = ["000001", "600519", "300750"] INDICATORS = { "ma_window": [5, 10, 20, 60], "rsi_window": 14, "macd": {"fast": 12, "slow": 26, "signal": 9}, } SCREEN_RULES = { "min_amount": 1e8, # 成交额下限,单位:元 "exclude_st": True, # 是否排除 ST "limit_up_limit": 0.095, # 接近涨停时忽略,避免追不进 } OUTPUT_DIR = "reports"为什么强调配置分离?因为每天运行时会频繁调整筛选条件。如果每次都改代码,很容易把正常功能改坏。配置独立之后,分析逻辑和业务参数互不影响,改起来也放心。
3. 技术指标计算:先做基础指标,再谈花样
3.1 从均线和涨跌幅开始
指标里最基础的是均线 MA 和涨跌幅。pandas 里用滚动窗口就能算:
# 示例:计算 5 日和 20 日均线 df["ma5"] = df["close"].rolling(window=5).mean() df["ma20"] = df["close"].rolling(window=20).mean() # 示例:计算日涨跌幅 df["pct_change"] = df["close"].pct_change() * 100这里有个容易忽略的点:rolling窗口是从当前行往前数 N 行,不是往后。如果数据顺序排反了,计算出来的均线就是“未来函数”,后面的筛选结果全都会错。计算之前先按日期升序排序,算完再考虑要不要倒序展示。这是一个非常隐蔽但后果很严重的坑。
3.2 MACD 和 RSI 的实现要点
MACD 由快线 EMA12、慢线 EMA26、差离值 DIF 和信号线 DEA 组成,最终得到柱状图。RSI 是相对强弱指标,常见窗口是 14 天。这些指标 pandas 没有现成函数,可以自己写,也可以用 ta 库。自己写的好处是理解每一步在算什么,不推荐无脑调库。
实现时要注意:EMA 带递推关系,第一天的初始值会影响前几天的结果。一般会多拉一部分历史数据参与计算,然后从中间截取结果,避免起始数据不稳定带来的误差。这也是为什么不要只拉最近 30 天数据——算 MACD 时,前面缺的样本会影响当前值。
3.3 特别容易出错的边界情况
- 新股上市不足 N 天:rolling 窗口算出来是 NaN,筛选时要跳过。
- 停牌股票:成交量可能为 0,或者当天没有行情,涨跌幅显示异常。
- ST 股票:名称带 ST,如果不打算纳入筛选,需要按名称过滤。
- 除权除息:前后价格不连续,复权因子要处理。免费数据源一般提供前复权或后复权接口,分析时统一用一种方式,不要混用。
这些边界情况不会天天遇到,但遇到一次就会让脚本崩溃或输出错误结果。稳妥做法是:清洗阶段把 NaN 和异常值标记出来,筛选阶段显式跳过,而不是直接让代码崩。你可以在脚本开头加一个数据校验函数,检查 DataFrame 是否为空、关键字段是否缺失、日期是否为最新交易日。校验不过就记录日志并退出,不要继续往下算。
注意:本文所有指标计算都只针对历史行情的数据处理练习,不构成任何投资建议。做这个项目的重心应该放在计算逻辑、数据清洗和工程稳定性上。
4. 筛选规则和报告输出:结果要让人能看懂
4.1 规则拆分,不要一锅煮
筛选规则建议拆成独立函数,每个函数只判断一个条件。例如:
def filter_st(df): return ~df["name"].str.contains("ST", na=False) def filter_amount(df, min_amount=1e8): return df["amount"] >= min_amount def filter_ma_trend(df): return (df["ma5"] > df["ma20"]) & (df["ma20"] > df["ma60"])规则拆开之后,可以单独验证每个条件的结果,也可以自由组合。后续想加一条“成交量放大”的规则,只需要新增一个函数,不影响已有逻辑。这种做法的核心收益是:出问题时能快速定位是哪条规则导致结果为空,而不是在一个几十行的复合条件里排查。
4.2 结果输出格式
最常用的输出是 CSV 和 Markdown 表格。CSV 方便继续处理,Markdown 方便直接贴到笔记或文档里。例如:
日期,代码,名称,收盘价,涨跌幅,ma5,ma20,信号 2025-01-10,600519,贵州茅台,1500.00,1.23,1490.50,1480.20,走强再进一步可以生成一个 reports/YYYY-MM-DD.md 文件,按规则分块展示结果:今日涨幅居前、均线多头排列、放量突破等。输出文件名带日期很重要,这样历史报告不会被覆盖。如果你用 Windows 写文件,建议统一指定encoding="utf-8-sig",Excel 打开 CSV 时中文不会乱码。
4.3 图表用在哪里
图表不是必须的,但 K 线叠加均线能让人快速看出一只股票的状态。matplotlib 画图时,可以输出整页图,也可以每天只给筛选结果里前几名画图。要注意图片数量不能太多,否则报告变得很重。我一般限制在 10 张以内,每张图标注股票代码和日期。
画图时有个细节:沪深股票代码带交易所前缀,文件命名要做处理。如果你同时用了多个数据源,时间戳和时区不一致会让 K 线错位,画图前先统一日期格式和时区。
5. 每天自动跑:定时调度、日志和失败重试
5.1 定时任务怎么选
daily_stock_analysis 的关键是 daily,所以定时执行是核心功能。常见方案有两种:
- 操作系统级:Linux 用 crontab,Windows 用任务计划程序。优点是稳定,能通过系统日志看到执行状态;缺点是换机器要重新配置。
- 程序内部:用 schedule 库或 APScheduler 在进程里定时触发。优点是跨平台、配置简单;缺点是进程必须一直运行,服务器重启后要记得拉起。
我的习惯是让脚本本身不依赖定时库,入口函数只负责“运行一次并退出”,由 crontab 或任务计划程序在固定时间调用。这样手动测试方便,排查问题也方便。
Linux 下的示例:
# 每个交易日 17:30 运行一次 30 17 * * 1-5 cd /path/to/daily_stock_analysis && python scheduler.py >> logs/cron.log 2>&1Windows 任务计划程序里,操作类型选“启动程序”,程序填 python.exe,参数填 scheduler.py 的绝对路径。注意不要把当前工作目录搞错,最好在入口脚本里用绝对路径拼数据目录。
5.2 日志是排查问题的第一入口
脚本跑完没有输出,你根本不知道是没执行、报错了,还是结果为空。所以日志一定要有。最简单的方式是用 Python logging 模块,同时输出到控制台和文件:
import logging logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s", handlers=[ logging.FileHandler("logs/run.log", encoding="utf-8"), logging.StreamHandler(), ], )日志里至少记录:数据拉取条数、指标计算耗时、筛选结果条数、输出文件路径。下次定位问题,先翻日志永远比对着代码猜快。尤其是定时任务,白天没时间盯,拿到日志一眼就能看出哪一步断了。
5.3 失败重试和幂等输出
数据接口偶尔超时是常态。获取数据时加一层重试,比如连续 3 次失败才放弃,每次间隔 2 秒。但重试逻辑写不好会重复插入数据,所以存储要注意幂等:要么用“日期 + 代码”作为唯一键,写入前检查;要么每天单独存一个文件,不覆盖历史。
另一个经验:不要把“数据没拉到”和“筛选结果为空”混为一谈。先检查数据是否拉取成功,再执行筛选。很多新手看到结果为空,第一反应是改筛选条件,实际问题是接口根本没返回数据。日志里单独打一条“数据拉取完成”的标记,能帮你快速区分这两种情况。
6. 实际跑起来最容易踩的坑
6.1 数据源报错:先看返回内容,不要只盯着异常
免费数据源偶尔返回空列表、字段缺失或类型错误。脚本里加一个校验函数,检查 DataFrame 是否为空、必填字段是否存在、日期是否为最新数据。校验不过就发一条告警,而不是继续往下算。
接口限流也很常见。全市场几千只股票逐个请求,很容易触发限制。应对办法是请求之间加短暂 sleep,或者优先使用数据源提供的批量接口,一次获取全部股票。不要一上来就开 50 个线程,接口容易拒绝,内存也会飙升。
6.2 指标算出来的数字对不上行情软件
同一个指标,你用 Python 算出来和行情软件显示不一致,多数原因不是代码错,而是参数或复权方式不同。比如 RSI 有 Wilder 平滑和简单平均两种算法,MA 有是否包含当天的区别。遇到对不上的情况,先检查你用的算法、窗口、复权方式和软件是否一致,不要急着怀疑数据源。
6.3 Windows 下中文编码问题
Windows 上跑 Python 写 CSV 时,默认编码可能是 GBK,数据里如果有特殊符号,写入会报 UnicodeEncodeError。建议写文件时统一指定encoding="utf-8-sig"。这个编码带 BOM,Excel 打开时中文不会乱码。
日志文件如果用 UTF-8 写入,而 Windows 记事本默认用 ANSI 打开,会显示乱码。这不是脚本 bug,是编辑器编码问题,换成 VS Code 或 Notepad++ 打开即可。
6.4 运行时间过长和资源占用
全市场行情分析比较耗时,瓶颈通常在网络请求,不是计算。第一次全量拉历史数据可能要好几分钟,之后每天增量拉取会很快。如果服务器配置低,减少并发请求数量,一次拉一小批,处理完再拉下一批。
我习惯把流程分成两段:先“全量补历史数据”,再“每天增量更新”。历史数据只在第一次运行时拉取,增量更新放进定时任务。这样每天任务量小,失败恢复也容易,不会因为一次网络抖动导致当天整个流程中断。
6.5 结果怎么看,项目怎么继续扩展
最后说一点和工程无关但很重要的事。daily_stock_analysis 的输出是技术指标和筛选列表,它解决的是“把数据整理成规则化结果”,不是“预测明天涨跌”。同一套规则在不同行情环境下表现差异很大,也没有哪种技术指标是稳定有效的。做这个项目时,建议把它当作数据工程练习,而不是寻找交易圣杯。
如果后续想扩展,可以按这个顺序来:接入多个数据源做交叉验证,增加回测模块验证规则的历史表现,把日报改成网页服务,再加入异常告警。每一步都不难,但要先把基础链路跑稳。
我个人更推荐的做法是:先挑两三只股票,把一个完整交易日跑通,确认数据、指标、筛选、报告、定时五部分都正常,再逐步扩到自选股和全市场。跑得稳比跑得多重要。踩过几次之后你会发现,很多问题不是工具能力不够,而是前置环境和输入数据没有处理干净。