Python股票分析选股系统实战:从数据获取到回测全解析

Python股票分析选股系统实战:从数据获取到回测全解析 简介本资源是一套面向高校学生与初级Python学习者的股票分析与选股实战项目适用于毕业设计、课程设计及量化入门开发。项目聚焦于历史行情数据获取与选股策略实现特别解决量化初学者在数据源稳定性与本地化导入上的痛点——全部依赖通达信本地导出数据规避网络平台积分、限流与停更风险。压缩包共14个文件63KB含11个Python脚本涵盖TDX数据读取、财务指标解析、技术图形绘制、策略模板与回测准备等核心功能、1份详细README.md文档、1个配置说明txt及1份LICENSE协议。已有255人学习下载项目代码经过实测可直接运行结构清晰、模块解耦便于理解数据处理流程并快速对接各类量化回测平台。 毕业设计想选股票分析方向但不知道从哪下手课程设计需要做一个“看起来厉害”又能刷高分的项目这篇文章帮你把基于Python的股票分析选股系统按实战标准拆透从数据源选型、指标计算、选股策略到回测框架再到论文和答辩需要的项目文档组织方式全都聊清楚。这套东西我在带毕设的过程中反复打磨过代码量不大但覆盖面完整既能体现数据分析能力、策略设计能力又能体现工程化思维。而且它天然关联一个非常热门的应用场景——量化投资往上可以接机器学习预测往下可以接自动化交易接口扩展空间很大作为毕设或课设的主题非常合适。1. 项目整体设计与技术选型1.1 为什么用Python做股票分析选Python不是因为它“流行”而是因为它是这个领域效率最高的解决方案。股票分析的核心操作是批量处理时序数据上千只股票、上百个交易日、几十个技术指标算完还要回测、画图、出报告。用传统方式做这个量级的计算代码会写得非常痛苦。而Python有pandas、NumPy这套数据科学栈向量化计算直接把循环消化掉了。举个例子计算250日均线如果不用pandas你得写至少三层循环。用pandas一行就完事了ma250 df[close].rolling(250).mean()这种差异不只是代码量的问题更是能否跑完整集测试的问题。真实研究场景中策略参数往往要遍历上百组每一组都要跑一次全市场回测。这时候如果基础计算慢十倍整个项目就没法继续了。另外一个现实因素是股票领域的大量算法交易框架、数据接口库都是Python优先支持比如大家经常看到的akshare、tushare、backtrader、qlib。用Python意味着你能站在这些开源生态的肩膀上做项目而不是自己从零造轮子。1.2 系统模块怎么划分做毕设最容易踩的坑就是所有代码堆在一个文件里数据获取、指标计算、策略逻辑、画图全搅在一起。项目最终还要交文档、做答辩代码组织混乱的话导师看你源码第一眼印象就不好。我自己一般把整个系统拆成六个模块每个模块解决一个独立问题模块核心职责关键库数据获取层从公开源拉取行情数据并做本地存储akshare / tushare数据存储层管理行情数据和选股结果的存取SQLite / pandas to_csv指标计算层计算常用技术指标封装统一接口pandas / numpy选股引擎根据策略规则对全市场打分排序pandas 自定义逻辑回测验证模块模拟历史行情验证策略真实表现backtrader / 自研简易回测可视化模块输出K线图、指标图、净值曲线和报告matplotlib / mplfinance这样划分的一个直接好处是每个模块都能独立测试数据层出问题不会牵扯策略层策略逻辑要改也不用动数据接口。对毕设来说还能在论文里专门写一章“系统架构设计”看起来专业很多。1.3 免费数据源的取舍做股票分析第一个要解决的就是数据去哪里拿。目前常用的免费A股数据接口有三个akshare、tushare、baostock。我实际对比过各有特点给大家整理一下怎么选。akshare无需注册token直接pip安装就能用接口非常多覆盖股票、基金、期货、宏观数据适合刚起步做实验。缺点是个别接口偶尔爬取不稳定数据格式也会因为网页结构变化而变化需要自己加容错。tushare数据质量高格式规范接口稳定但需要注册账号拿token而且部分高频或深度数据有积分门槛。做毕设如果时间紧可以考虑直接用它的基础积分接口日线行情够用。baostock免费且数据源比较稳定有完整的前后复权数据对回测来说很有用。接口写法相对繁琐一点胜在数据干净。我推荐新手用akshare起步把系统调通之后如果想更稳再切换到tushare或baostock。重点是你要在数据层做一个统一接口这样换数据源的时候只改一处而不是全项目到处改。我自己就是这么做的写了一个get_daily_data(stock_code, start_date, end_date)的函数内部调akshare所有上层模块只认这个函数。后来发现某个接口限流了就直接把函数内部改成baostock其余代码一行未动。2. 核心细节解析与实操要点2.1 数据获取层的完整实现数据层是整个系统的地基地基建不好后面策略再漂亮都是空中楼阁。我用akshare做一个最简单的数据拉取封装核心代码是这样的import akshare as ak import pandas as pd from datetime import datetime, timedelta def fetch_daily(stock_code600519, start_date20150101, end_dateNone): 拉取个股日线数据返回标准格式DataFrame if end_date is None: end_date datetime.now().strftime(%Y%m%d) # akshare的A股接口参数需要写完整 df ak.stock_zh_a_hist( symbolstock_code, perioddaily, start_datestart_date, end_dateend_date, adjustqfq # 前复权数据 ) # 统一列名方便上层调用 df.columns [ date, open, close, high, low, volume, amount, amplitude, pct_change, change, turnover ] df[date] pd.to_datetime(df[date]) return df这段代码看起来简单但有几个细节值得注意第一复权处理。如果只拿原始价格做回测遇到分红送股的日子K线会出现跳空均线指标也会失真。用adjustqfq拿前复权数据等于把历史价格按当前价格做了调整消除了这个影响。不过你要知道前复权数据会随着最新价变动而整体漂移严谨的量化研究一般用后复权数据回测这里对毕设来说前复权已经够用。第二列名统一。这算是我自己踩出来的经验。不同数据源返回的列名完全不同有的叫日期有的叫trade_date如果不统一后面指标计算代码会被各种判断塞满。在上游把所有列名标准化你的指标层就能只认这套标准。第三异常处理。akshare偶尔会因网络波动抛异常拉起全市场股票名单时经常断。建议在调用处加个try-except断了的股票自动跳过失败的记录到日志里最后集中重试不要因为一只股票挂了就中断整个任务。2.2 技术指标怎么算才靠谱选股系统常用指标就那么几个均线族MA、EMA、MACD、KDJ、RSI、布林带。每个指标网上都有公式但用pandas实现时有不少细节算错了指标你根本看不出来策略结果却是错的。MA简单移动平均线是最基础的核心参数是窗口大小df[ma5] df[close].rolling(window5).mean() df[ma20] df[close].rolling(window20).mean() df[ma60] df[close].rolling(window60).mean()这里注意pandas的rolling()默认会在开头产生NaN值——窗口不满的情况下没法计算均值。如果要处理这种边界可以用min_periods参数rolling(20, min_periods5)表示至少5个数据点就出值。做选股时不建议用min_periods补齐免得把上市没几天的新股硬算进策略。MACD看似复杂本质就是两次指数移动平均。EMA的pandas实现用ewm()函数# MACD(12, 26, 9) ema12 df[close].ewm(span12, adjustFalse).mean() ema26 df[close].ewm(span26, adjustFalse).mean() df[dif] ema12 - ema26 df[dea] df[dif].ewm(span9, adjustFalse).mean() df[macd_bar] (df[dif] - df[dea]) * 2这里有个常见的坑adjustFalse这个参数。EMA有两种计算公式——调整式和不调整式。pandas默认adjustTrue它会用更复杂的权重修正历史值导致序列在起始段振荡大。对股票分析来说标准写法是adjustFalse这样才是我们熟悉的那种MACD。RSI相对强弱指标的实现方式很多最常用的是SMA简化法。需要注意RSI计算里有个涨跌均值处理时千万别用mean()直接算应该用滚动平均delta df[close].diff() gain delta.clip(lower0) loss -delta.clip(upper0) avg_gain gain.rolling(window14).mean() avg_loss loss.rolling(window14).mean() rs avg_gain / avg_loss df[rsi] 100 - (100 / (1 rs))2.3 选股策略的设计思路有了指标数据怎么把它变成“选哪只股票”的决策这里我强烈建议毕设不要用太复杂的机器学习模型而是先用多因子打分模型把整套流程跑通原因有两个一是可解释性强答辩时你清楚每个因子在干什么二是代码量适中不用引入sklearn额外增加复杂度。多因子打分模型的逻辑很直观选几个你觉得有预测力的维度每维度对每只股票打分加权求和得到总分按总分从高到低排序取前N名作为候选股票。我实际写过一个简化版本选了四个因子动量因子近20日涨幅捕捉顺势趋势趋势因子价格相对60日均线的偏离度捕捉均线多头排列量能因子近5日平均成交量相对近60日平均成交量的比捕捉放量信号波动率因子近20日收益率标准差反向打分规避大幅震荡核心代码对因子做z-score归一化然后加权求和。这里有个细节我实测很重要——因子方向要一致。比如你要“高动量加分”那价格相对均线的偏离度就得按正值处理波动率你要“低波动加分”就得取负数再做归一化。方向不一致加权求和就变成一锅粥。def score_stocks(df_all, factors): scores pd.DataFrame(indexdf_all[code].unique()) for factor, weight in factors.items(): # 每个因子先做z-score标准化方向和权重由策略指定 s (df_all.groupby(date)[factor].rank() .groupby(df_all[date]).transform( lambda x: (x - x.mean()) / x.std() )) scores[factor] s scores[total] sum( scores[factor] * weight for factor, weight in factors.items() ) return scores.sort_values(total, ascendingFalse)注意代码里用了两次groupby其实更标准做法是每个因子按日期分组做截面标准化这样每天的分数是可比的。但实际项目里数据量大了这种写法会很慢我有次跑全市场数据直接内存爆炸。优化思路是先横向拼接所有因子的长表再统一groupby(date)一次处理。2.4 回测框架是怎么避坑的选股策略到底靠不靠谱不能靠感觉必须回测。回测就是拿历史行情模拟交易把策略结果跑一遍。我建议毕设先用最简单的自研回测不要一上来就backtrader那个库虽然有现成的但它的API设计有一定学习成本。自研回测的核心逻辑其实很清晰def backtest(df, signal_col, initial_cash100000): cash initial_cash position 0 equity_curve [] for i in range(len(df)): # 先基于今天已知的数据判断是否调仓 # 关键只能用当天已经发生的信号不能用未来数据 if df[signal_col].iloc[i] and position 0: position cash // df[close].iloc[i] cash - position * df[close].iloc[i] elif not df[signal_col].iloc[i] and position 0: cash position * df[close].iloc[i] position 0 equity_curve.append(cash position * df[close].iloc[i]) return equity_curve但这个简易版本有个致命问题没考虑次日开盘价成交。真实交易中你不可能用收盘价瞬间成交信号出现当天收盘价你是拿不到的至少量化上高频才对。严谨的做法是今天产生信号明天开盘价买入。这个“未来函数”问题导师一眼就能看出来如果你在论文里没说明白这一处就可能致命扣分。改进版本如下信号出现后下一次循环的开盘价成交。这种“T1”的模拟在资管机构里也是底线要求。另一个容易忽略的参数是手续费和滑点。虽然现在有的券商佣金低到万几但单边万2.5加上印花税卖出千1加上滑点一年下来对收益影响不小。回测时至少按双边千1~千2估算不然策略在回测里赚钱实盘却在亏。3. 实操过程与核心环节实现3.1 项目目录与代码文件结构一个结构清晰的项目第一眼就能让答辩老师觉得“这学生工程素养不错”。推荐目录组织方式如下stock_selector/ ├── main.py # 主入口跑全流程 ├── requirements.txt # 依赖清单 ├── README.md # 项目说明与使用指南 ├── config.py # 全局配置股票池、日期、参数 ├── data_fetcher.py # 数据获取封装 ├── data_processor.py # 数据清洗与指标计算 ├── strategy.py # 选股策略引擎 ├── backtest.py # 回测引擎 ├── visualizer.py # 可视化模块 ├── docs/ │ ├── design.md # 系统设计文档 │ ├── user_guide.md # 使用说明 │ └── api.md # 接口说明 ├── data/ # 本地数据缓存 └── output/ # 结果输出图表、CSV每个文件职责单一main.py只负责串联流程不写任何业务逻辑。使用说明文档单独放docs/目录这样源码、文档、数据各自分离查阅非常方便。3.2 环境搭建与依赖准备环境准备这块我自己碰到过比较坑的是pandas版本差异。如果你用的pip install -r requirements.txt一次性安装建议在requirements.txt里固定版本号akshare1.12.0 pandas1.5.0 numpy1.23.0 matplotlib3.6.0 mplfinance0.12.9b0安装命令很简单cd stock_selector pip install -r requirements.txt需要提一句akshare依赖的接口有时候会升级如果某天跑报错了先检查是不是akshare版本太老。我遇到过几次解决方法是pip install akshare --upgrade。如果用的是Anaconda环境建议先建一个干净环境再装conda create -n stock python3.9 conda activate stock pip install -r requirements.txtPython版本建议3.9~3.11之间太新的版本偶尔会有库兼容问题太老则会影响后续扩展。3.3 数据存储与数据库设计本地数据缓存这块最简单方案是用CSV文件把每只股票历史数据存成一个data/600519.csv。但做毕设建议把事情做得体面一点用SQLite存结构化数据。SQLite的好处是零配置、文件型不需要单独装数据库服务答辩演示时也方便打包带走。两张核心表股票基本信息表、日线行情表。CREATE TABLE stock_basic ( ts_code TEXT PRIMARY KEY, name TEXT, industry TEXT, list_date TEXT ); CREATE TABLE daily_price ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts_code TEXT, trade_date TEXT, open REAL, high REAL, low REAL, close REAL, volume REAL, amount REAL, UNIQUE(ts_code, trade_date) );强调一下日线行情表一定要加UNIQUE约束否则重复执行更新时数据会翻倍。我之前调试时反复跑数据拉取脚本结果回测数据膨胀了一倍还不自知直到净值曲线出现奇怪的跳变才排查出来。3.4 完整跑通选股与回测流程系统跑通一次全流程的逻辑大概是这个顺序初始化股票池从数据源获取沪深主板股票列表剔除ST、上市不足60个交易日的新股更新行情数据遍历股票池调用fetch_daily()拉取最新的日线数据入库计算指标对每只股票计算MA、MACD、RSI、布林带等指标执行选股以最近一个交易日为基准对所有股票按多因子模型打分输出Top N候选回测验证选出的Top N股票按信号回测最近一年表现输出报告生成净值曲线图、持仓明细、绩效指标表main.py里只需要这样串联def main(): # 1. 初始化股票池 pool get_stock_pool() # 2. 更新行情 update_all_stocks(pool) # 3. 计算指标 indicators compute_all_indicators() # 4. 选股打分 selected run_selector(indicators) # 5. 回测验证 results run_backtest(selected) # 6. 输出报告 generate_report(results)跑完后output/目录下会生成两个文件一份selected_stocks.csv包含选出的股票代码、名称、综合得分和各因子得分另一份backtest_report.png包含资金净值曲线和基准指数对比。答辩时直接贴这两张图视觉效果好而且背后有完整推导过程支撑。4. 常见问题与排查技巧实录4.1 数据层面的坑数据接口调用报错akshare接口类型是动态维护的如果某天白屏或报KeyError多半是数据格式变了。排查方法很简单先print返回的DataFrame前几行看列名再确定是定位问题还是代码问题。涨跌停、停牌股票的数据缺失停牌期间没有交易数据这个要处理好。选股时如果一只股票停牌了它当天的收盘价其实是停牌前收盘价这时买入信号会失真。建议在选股前过滤掉近5日成交量异常的股票即停牌股直接跳过。除权除息复权问题这个问题前面提过值得再强调一次。源码里的均线、MACD全部要基于复权价算否则分红日那个“价格断层”会被误认为下跌趋势。如果用的是前复权数据注意最新日期附近的复权因子不稳定做长区间回测时要谨慎。4.2 指标计算的边界问题日常调试中指标计算常见的坑集中在数据长度不够和NaN值传播。做RSI这种需要14日前涨跌的指标如果某只股票上市不满14天计算结果为NaN。选股时如果直接忽略NaN行会把上市时间短但波动较大的次新股全选出来效果很差。建议的策略是先过滤持仓天数再计算指标。MACD的起始段有个“预热期”ewm在序列开头会慢慢收敛到正常值。如果你用的是近两年数据其实不太影响但只取近3个月的日线开头段的DIF/DEA就开始乱飘。解决办法是拉取数据时多留一年“预热数据”计算完指标后裁剪出实际分析区间。这个思路叫warm-up量化领域很常用。4.3 回测结果的“看上去很美”我做这个项目时最大的教训就一句话回测收益很高不代表策略好很可能是bug在给你发钱。常见问题未来函数。选股信号用了当天的收盘价却在同一天以收盘价成交——看起来当天就赚钱了实际上信号和成交发生在同一时刻这在实盘中根本不可能。严重性前面讲过。幸存者偏差。选股票池时直接用了当前存在的公司列表退市的公司全被忽略回测结果天然比真实市场好。这是毕设回测最容易被挑刺的点严格做法是用历史时点的股票池成分但数据获取成本较高。低成本缓解方案是在论文里明确这个限制并在选股时排除ST股和长期停牌股。参数过拟合。反复测试发现“60日均线RSI14”效果最好这看起来是发现了规律但很可能只是数据集上的偶然。缓解办法是分两段时间做样本内外测试比如前70%数据调参后30%数据做验证验证集不再改参数。这个方法写进论文里是专业度的得分点。4.4 环境与依赖的安装问题环境安装看似简单但很多同学卡在这一步。常见的几个问题pip安装慢或超时用国内镜像源pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simplematplotlib中文乱码需要额外配置中文字体否则图上的标题全是方块。解决思路是下载一个中文字体比如SimHei放到matplotlib字体目录并在代码里注册。akshare导入报错有次是依赖的py_mini_racer版本冲突直接pip uninstall py_mini_racer重装对应的版本好了。建议在README里把环境搭建过程按步骤写清楚尤其是镜像源和字体配置这类的细节因为毕设是要给导师检查的一份清晰的使用说明能省很多指导时间。5. 项目文档怎么组织源码写完之后项目文档是决定这个毕设成绩的另一个关键。文档我建议分成三份各管各的事README.md项目入口文档写清楚项目是干什么的、能做什么、环境要求怎么搭、怎么跑通。重点是让一个从未接触过这个项目的人5分钟内能把项目跑起来。design.md设计文档对应论文里的系统设计章节从数据获取、指标计算、选股策略到回测每个模块是干什么的、为什么这么设计、用到哪些关键技术。建议配合文字叙述放一两张系统架构图。我记得答辩时导师会特别关注“为什么选这个方案”所以一定要把选型对比写进去。user_guide.md使用说明对应论文里的实验章节写清楚用这个系统执行的完整流程每一步操作之后能看到什么结果。用户按着文档走一遍就能复现你的全部实验结果这在论文里叫“实验可复现性”是学术规范也是导师考察的重点。写文档时有个小技巧先写好README再写代码。如果你写完代码再写文档大概率会漏掉很多只有当时查资料才懂的细节而这些细节恰恰是读者最需要的。6. 后续扩展方向如果论文写完后还有精力或者你想把这个项目作为求职作品集有几个扩展方向非常加分引入更多因子把行业中性因子、规模因子加进来对标成熟的多因子模型接入机器学习股票预测在技术指标基础上喂给随机森林或XGBoost分类模型选股实时数据与自动选股把选股流程定时运行每天开盘前自动出报告部署成Web服务用Flask或Streamlit做一个可视化选股面板在线输入参数看结果其中Streamlit方案最推荐代码量不多几分钟就能把选股结果做成一个带交互界面的Web应用加上下拉选参数、动态出图演示效果非常棒。我个人在实际操作中的体会是股票分析选股这个项目代码本身并不难难的是数据处理的严谨性和策略评估的客观性。与其把功夫花在多漂亮的模型上不如踏踏实实把数据清洗、指标计算、回测逻辑做扎实。真正能让你学到东西的往往是那些调试过程中发现的“坑”——我上面写的这些问题基本上都是自己踩过之后才真正理解的。最后的建议还是那句话做这个项目先把回测跑通再把净值曲线打印出来亲手检查几次买卖信号是否真的是“未来发生的”这一步做好了整个项目的专业度就立住了。本文还有配套的精品资源点击获取