
简介这是一套面向量化投资初学者与数据分析爱好者的股票历史数据分析实战项目围绕数据采集、存储、可视化与策略选股四大环节展开帮助读者搭建从行情获取到涨跌预测的完整链路。资源包共39个文件以25个Python脚本为核心辅以4个CSV样例数据、2个XML配置、1个YAML配置及HTML页面等压缩包约364KB目录按业务、工具、策略、数据库等模块划分结构清晰便于按功能查阅。项目覆盖接入行情接口抓取开高低收与成交量、写入CSV和MySQL、绘制K线图与成交量图并基于线性回归、时间序列、随机森林等模型做涨跌预测同时提供多因子选股策略的评分与排名思路。目前已有414人学习下载适合想通过一个可运行项目打通数据采集、存储、可视化到策略预测全流程的读者参考借鉴。1. 从一份 stock-sky.zip 说起股票历史数据采集到预测选股的完整链路很多人拿到stock-sky.zip这类项目包第一反应是解压、装依赖、跑main.py然后被一堆报错劝退。其实这个标题已经把整条链路说清楚了采集股票历史数据 → 落地成 csv 和 MySQL 两种存储 → 画 K 线图和成交量图 → 用策略预测涨跌 → 选股。它解决的不是预测明天涨停这种玄学问题而是把一套可复现的量化工作流搭起来让你能自己换数据源、换策略、换股票池。适合谁看会一点 Python、装过 MySQL、想认真做量化而不是听消息炒股的人。新手可以照着命令一步步跑通熟手可以直接跳到参数调优和避坑章节。下面我按数据怎么来、怎么存、怎么画、怎么算、怎么选的顺序把这条链路拆开讲中间会穿插我自己踩过的坑比如 MySQL 连接报error 2002、预测结果每次不一样这类血泪经验。2. 数据采集与双通道存储akshare 拉数、csv 落盘、MySQL 入库2.1 为什么选 akshare 而不是自己爬网页股票数据接口 api 这几年变化很快自己写爬虫抓行情页对方一改版你就得重写维护成本极高。常见做法是用现成的开源数据接口库akshare 就是其中比较省心的一个它封装了行情、财务、指数等多类接口返回的直接是 pandas DataFrame省掉解析 HTML 的功夫。选它的理由很实际一是免费、无需申请 key二是字段命名统一日期 / 开盘 / 收盘 / 最高 / 最低 / 成交量这套列名基本固定后面存 csv、建表、画图都能直接复用三是社区活跃接口失效时通常很快有人修。代价是它依赖上游数据源偶尔会限流或字段微调所以采集层一定要做异常捕获和重试不能裸奔。安装很简单pip install akshare pandas sqlalchemy pymysqlakshare负责取数pandas负责清洗和落盘sqlalchemy pymysql负责写 MySQL。这四个是整条链路的地基先装齐再往下走。2.2 采集单只股票的历史日线并落成 csv先跑通一只股票确认数据形态再谈批量。下面这段是采集平安银行000001近几年的日线并写成 csv 的最小可用代码import akshare as ak import pandas as pd def fetch_daily(symbol: str, start: str, end: str) - pd.DataFrame: # symbol 不带市场前缀如 000001start/end 格式 20200101 df ak.stock_zh_a_hist( symbolsymbol, perioddaily, # daily/weekly/monthly start_datestart, end_dateend, adjustqfq # 前复权画K线必须用复权价 ) # akshare 返回中文列名统一改成英文方便后续建表和画图 df df.rename(columns{ 日期: date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: volume, 成交额: amount, 涨跌幅: pct_chg }) df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) return df[[date, open, high, low, close, volume, amount, pct_chg]] if __name__ __main__: data fetch_daily(000001, 20200101, 20241231) data.to_csv(000001_daily.csv, indexFalse, encodingutf-8-sig) print(data.tail())逻辑说明adjustqfq是关键参数前复权把历史价格按分红送股调整过否则 K 线会出现巨大跳空缺口画出来完全没法看。encodingutf-8-sig是为了让 Excel 打开 csv 不乱码这是导入 csv 文件时最常见的翻车点。period改成weekly或monthly就能拿周线月线字段结构一致。参数上要注意start_date和end_date是字符串且不带横杠写成2020-01-01会直接报错。另外 akshare 单次返回行数有限跨度特别大时分段拉取再concat更稳。2.3 建 MySQL 表并批量入库csv 适合单机快速验证但要做选股、要按条件过滤几千只股票MySQL 才是正解。先建库建表CREATE DATABASE IF NOT EXISTS stock_sky DEFAULT CHARACTER SET utf8mb4; USE stock_sky; CREATE TABLE IF NOT EXISTS daily_kline ( id BIGINT AUTO_INCREMENT PRIMARY KEY, symbol VARCHAR(10) NOT NULL, trade_date DATE NOT NULL, open DECIMAL(10,3), high DECIMAL(10,3), low DECIMAL(10,3), close DECIMAL(10,3), volume BIGINT DEFAULT 0, amount DECIMAL(20,2) DEFAULT 0, pct_chg DECIMAL(8,3) DEFAULT 0, UNIQUE KEY uk_symbol_date (symbol, trade_date) ) ENGINEInnoDB;UNIQUE KEY uk_symbol_date是后悔药重复采集同一只股票时用INSERT ... ON DUPLICATE KEY UPDATE就能幂等覆盖不用先删表。volume设了DEFAULT 0避免空值插入报错这也是 mysql 设置默认值为 0 的典型用法。入库代码from sqlalchemy import create_engine import pandas as pd engine create_engine( mysqlpymysql://root:yourpass127.0.0.1:3306/stock_sky?charsetutf8mb4 ) def save_to_mysql(df: pd.DataFrame, symbol: str): df df.copy() df.insert(0, symbol, symbol) df df.rename(columns{date: trade_date}) # 用临时表 REPLACE 实现幂等写入 df.to_sql(daily_kline, engine, if_existsappend, indexFalse, methodmulti, chunksize1000) save_to_mysql(data, 000001)methodmulti把多行合并成一条 INSERT几千行数据入库速度能快好几倍。chunksize1000防止单条 SQL 过长被 MySQL 拒绝。如果表里已有唯一键重复数据会报错稳妥做法是先to_sql到临时表再INSERT IGNORE或REPLACE INTO主表这个在批量采集时一定要处理。3. K 线图与成交量图用 mplfinance 画出能看的图3.1 画图库怎么选mplfinance 还是 pyecharts热搜里常出现 hqchart 控件 k 线图、react uplot k 线图那些偏前端交互。做数据分析阶段我一般用mplfinance它专为金融图表设计K 线、成交量、均线一次画完代码量极少。要交互式网页图再考虑 pyecharts 或前端方案但那是展示层的事先把分析图跑通。pip install mplfinance3.2 一张图里叠 K 线和成交量import mplfinance as mpf import pandas as pd df pd.read_csv(000001_daily.csv, parse_dates[date], index_coldate) # 计算5日、20日均线 df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() mpf.plot( df, typecandle, # 蜡烛图 volumeTrue, # 下方叠加成交量 mav(5, 20), # 自动画均线 stylecharles, title000001 日K与成交量, ylabel价格, ylabel_lower成交量, figsize(14, 8), savefig000001_kline.png )逻辑说明volumeTrue让成交量自动排在 K 线下方共享 x 轴这是最省事的做法。mav(5,20)直接传均线周期不用自己画线。type还能换成line、ohlc。savefig直接出图方便批量生成后归档。参数坑index必须是 DatetimeIndex否则 x 轴会变成序号而不是日期。如果 csv 里日期列没解析parse_dates[date]这步不能省。成交量单位是股数值很大时 y 轴刻度会挤可以自己传volume_panel或对 volume 做缩放。3.3 批量出图与股票切换选股场景下往往要给几十只股票各出一张图。把上面的逻辑包成函数循环股票列表即可文件名带上 symbol 便于检索。如果要做成交互式切换股票那是前端 hqchart 或 uplot 的活后端只需提供按 symbol 查询的接口返回 json 给前端渲染。分析阶段不必上交互先把静态图批量产出人工快速过一遍形态效率反而更高。4. 策略预测涨跌特征、标签与那个每次结果不一样的坑4.1 把预测问题定义清楚预测涨跌必须先定义什么叫涨。常见做法是用当日收盘后能拿到的特征预测下一交易日收盘价相对今日收盘的涨跌方向标签1表示涨、0表示跌。特征用均线、涨跌幅、成交量变化、波动率等。这一步定义不清后面全是白忙。import numpy as np def build_features(df: pd.DataFrame) - pd.DataFrame: df df.copy() df[ret1] df[close].pct_change(1) df[ret5] df[close].pct_change(5) df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() df[ma_ratio] df[ma5] / df[ma20] df[vol_ma5] df[volume].rolling(5).mean() df[vol_ratio] df[volume] / df[vol_ma5] df[volatility] df[ret1].rolling(10).std() # 标签下一日是否上涨 df[label] (df[close].shift(-1) df[close]).astype(int) return df.dropna()shift(-1)是把明天的结果对齐到今天这是时序预测里最容易搞反的地方一旦写成shift(1)就变成用未来预测现在回测收益会好得离谱实盘必亏。4.2 用 KNN 跑一个能复现的基线热搜里 knn 算法 股票量化分析出现频率很高KNN 简单、无需训练、适合当基线。关键是固定随机种子和标准化否则就会出现模型预测股票涨跌 每次结果不一样。from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score import numpy as np np.random.seed(42) # 固定随机种子 feat_cols [ret1, ret5, ma_ratio, vol_ratio, volatility] data build_features(df) X data[feat_cols].values y data[label].values # 时序数据不能随机打乱按时间切分 split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] scaler StandardScaler().fit(X_train) X_train scaler.transform(X_train) X_test scaler.transform(X_test) model KNeighborsClassifier(n_neighbors5) model.fit(X_train, y_train) pred model.predict(X_test) print(准确率:, accuracy_score(y_test, pred))逻辑说明StandardScaler必须只用训练集拟合再 transform 测试集否则数据泄漏。n_neighbors5是起点可以网格搜索。时序切分不能用train_test_split(shuffleTrue)那等于用未来数据训练。参数坑KNN 对特征尺度极敏感不标准化结果会乱跳random_state不固定某些模型每次跑结果都不同这就是每次结果不一样的根源。准确率能到 52%~55% 就算不错别指望 80%那是过拟合。4.3 策略回测与选股落地预测出方向后选股逻辑就是对股票池里每只股票跑一遍模型取预测为涨、且置信度KNN 可用近邻投票比例靠前的若干只组成当日候选池。回测时按这个池子等权买入、次日或持有 N 日卖出统计累计收益和最大回撤。def pick_stocks(symbols, model, scaler, top_n5): scores [] for sym in symbols: d build_features(load_history(sym)) if len(d) 30: continue x scaler.transform(d[feat_cols].values[-1:]) prob model.predict_proba(x)[0][1] # 上涨概率 scores.append((sym, prob)) scores.sort(keylambda t: t[1], reverseTrue) return scores[:top_n]predict_proba给出概率而非硬标签排序选股更合理。注意每只股票都要用同一套 scaler 和特征列否则结果不可比。5. 避坑与排查那些让项目跑不起来的常见问题5.1 MySQL 连接报 error 2002现象error 2002 (hy000): cant connect to local mysql server through socket /tmp/mysql.sock。 原因MySQL 服务没启动或客户端找的 socket 路径和服务端不一致。 解决先systemctl status mysql看服务状态没起就systemctl start mysql确认my.cnf里 socket 路径连接串里显式写127.0.0.1:3306走 TCP 而不是 socket。5.2 导入 csv 到 MySQL 中文乱码现象csv 用 Excel 打开正常导入后中文变问号。 原因csv 是 GBK 编码而表是 utf8mb4编码不匹配。 解决导出时统一用utf-8-sig导入用LOAD DATA ... CHARACTER SET utf8mb4建库时就指定DEFAULT CHARACTER SET utf8mb4。5.3 复权方式选错导致 K 线断裂现象K 线图上出现巨大跳空均线全乱。 原因用了不复权价格遇到分红送股就断层。 解决采集时adjustqfq前复权如果要做长周期回测注意前复权价格会随最新数据变化历史回测结果需要重新计算。5.4 预测结果每次都不一样现象同一份数据跑两次准确率差好几个点。 原因随机种子没固定、数据切分打乱、特征未标准化。 解决np.random.seed(42)、random_state42时序数据按时间切分标准化只在训练集拟合。5.5 批量采集被限流或接口报错现象循环采集几百只股票中途大量失败。 原因请求过快触发上游限流或某只股票代码不存在。 解决每次请求间time.sleep(0.5~1)用 try/except 包住单只采集失败记录到日志继续下一只不要整体中断。6. 进阶把单机脚本变成可维护的选股流水线跑通单只股票只是起点。真正要长期用得把采集、入库、画图、预测拆成独立模块用调度器每天收盘后自动跑。我一般会加一层配置股票池放一个 txt 或表里采集和选股都读它换池子不用改代码。验证策略是否靠谱别只看准确率要看分层收益把预测上涨概率分成 5 档看最高档的实际次日平均收益是否显著高于最低档。如果各档收益没区别说明模型没学到东西特征得重做。def evaluate_by_bucket(data, model, scaler, feat_cols, n_bucket5): X scaler.transform(data[feat_cols].values) prob model.predict_proba(X)[:, 1] data data.assign(probprob) data[bucket] pd.qcut(data[prob], n_bucket, labelsFalse) # 下一日真实收益 data[next_ret] data[close].pct_change(-1) return data.groupby(bucket)[next_ret].mean()qcut按概率分位切桶pct_change(-1)算下一日收益。如果最高桶收益明显更高策略才有继续调的价值否则先回去检查特征和标签定义。一个具体技巧把vol_ratio量比和ma_ratio均线比做交叉特征比如vol_ratio * ma_ratio往往比单特征更能区分趋势启动。特征工程比换模型划算得多。我自己最大的教训是别一上来就追求复杂模型先用 KNN 或逻辑回归把整条链路跑通确认数据没错、标签没错、回测框架没错再考虑上树模型或神经网络。链路对了简单模型也能出结果链路错了再复杂的模型都是自欺欺人。希望帮到你。本文还有配套的精品资源点击获取