ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

股票量化数据管道实战:akshare采集、CSV/MySQL存储与K线选股

股票量化数据管道实战:akshare采集、CSV/MySQL存储与K线选股 简介这是一套面向量化投资初学者与金融数据分析爱好者的股票历史数据分析实战项目围绕数据采集、存储、可视化、涨跌预测与选股策略展开帮助读者搭建从原始行情到决策支持的完整链路。资源包共39个文件以25个Python脚本为核心辅以4个CSV样例数据、2个XML配置、1个YAML配置、1个HTML页面及Pipfile等依赖管理文件整体约364KB目录按业务、策略、数据库、界面等模块划分结构清晰便于按功能检索。项目覆盖K线图与成交量图绘制、CSV与MySQL双通道存储、基于策略的涨跌预测和选股评分等关键环节读者可据此理解数据采集接口调用、结构化落库、图表渲染与模型训练验证的衔接方式并参考现有模块组织自己的分析流程。目前已有414人学习下载适合需要完整项目骨架与排错思路的中级学习者。1. 从一份股票数据工具包说起采集、入库、画图、选股到底怎么串起来很多人做股票量化第一步就卡在数据上接口调不通、字段对不上、CSV 编码乱码、MySQL 连不上好不容易把数据存进去画 K 线又是一堆坑。这份stock-sky.zip工具包解决的正是这条链路——从历史行情采集到落地 CSV 和 MySQL 两种存储再到 K 线图与成交量图渲染最后用策略做涨跌预测和选股。它适合两类人一是刚入门量化、想跑通完整流程的新手二是已有策略想法、但缺一套可复用数据管道的从业者。整条链路里采集是地基存储决定后续查询效率画图是验证手段预测和选股才是最终目的。下面按实际拆包顺序把每个环节的参数、代码和坑讲透。2. 历史数据采集akshare 拉取与字段清洗的完整链路2.1 为什么选 akshare 而不是爬虫或付费接口股票历史数据采集常见三条路自己写爬虫抓网页、买付费数据接口、用开源财经数据库。爬虫的问题是页面结构一变就全废维护成本高付费接口稳定但按量计费做回测动辄几百万条数据成本压不住。akshare 属于开源财经数据接口库覆盖 A 股、港股、美股、期货、基金等品种日线、分钟线、复权因子都能拿到对个人量化来说性价比最高。常见做法是用ak.stock_zh_a_hist拉 A 股日线参数里symbol传六位代码period选dailystart_date和end_date控制区间adjust决定复权方式——qfq前复权、hfq后复权、空字符串不复权。做回测一定要用前复权否则除权除息那天会出现价格跳空策略信号全是假的。这个坑我见过太多人踩回测收益虚高实盘一跑就露馅。2.2 采集脚本与字段标准化import akshare as ak import pandas as pd def fetch_stock_daily(symbol: str, start: str, end: str, adjust: str qfq) - pd.DataFrame: 拉取单只股票日线数据 symbol: 六位代码如 600519 start/end: YYYYMMDD 格式 adjust: qfq 前复权 / hfq 后复权 / 不复权 df ak.stock_zh_a_hist( symbolsymbol, perioddaily, start_datestart, end_dateend, adjustadjust ) # akshare 返回中文列名统一改成英文方便后续入库和策略调用 df df.rename(columns{ 日期: trade_date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: volume, 成交额: amount, 振幅: amplitude, 涨跌幅: pct_change, 涨跌额: change, 换手率: turnover }) df[trade_date] pd.to_datetime(df[trade_date]) df[symbol] symbol # 按日期升序回测时按时间遍历才不会乱序 df df.sort_values(trade_date).reset_index(dropTrue) return df if __name__ __main__: data fetch_stock_daily(600519, 20200101, 20241231) print(data.head()) print(f共 {len(data)} 条记录)这段代码的逻辑分三步调接口拿原始数据、重命名列、补 symbol 字段并排序。参数上adjustqfq是回测标配start_date和end_date建议按年分段拉一次拉十年数据接口容易超时。sort_values这步别省akshare 偶尔返回的顺序不严格策略里做 rolling 计算时顺序错了结果全错。2.3 批量采集与限频处理单只股票拉完不算完选股需要全市场数据。批量采集要控制频率akshare 底层走的是公开接口请求太密会被限流。我一般用time.sleep(0.5)在每只股票之间加间隔全市场五千多只股票跑一遍大概四十分钟。如果追求速度可以多进程分片但进程数别超过 4否则照样被掐。import time import os def batch_fetch(symbols: list, start: str, end: str, out_dir: str): os.makedirs(out_dir, exist_okTrue) for i, sym in enumerate(symbols): try: df fetch_stock_daily(sym, start, end) df.to_csv(f{out_dir}/{sym}.csv, indexFalse, encodingutf-8-sig) print(f[{i1}/{len(symbols)}] {sym} 完成{len(df)} 条) except Exception as e: print(f{sym} 失败: {e}) time.sleep(0.5) # 限频别删encodingutf-8-sig是为了 Excel 打开 CSV 不乱码这个细节后面避坑章节还会提。失败重试建议单独记一个失败列表跑完再补不要在主循环里无限重试容易卡死。3. 双通道存储CSV 落地与 MySQL 入库的参数配置3.1 CSV 存储的编码与分片策略CSV 适合做原始数据备份和快速查看但不适合做频繁查询。存 CSV 有两个参数必须定死编码用utf-8-sig分隔符用逗号。如果用默认的utf-8Windows 下 Excel 打开中文列名会乱码这是血泪经验。另外单文件别太大一只股票十年日线也就两千多条按股票代码分文件最合理文件名直接用600519.csv后续读取时用代码拼路径比维护一个总表方便。读取时用 pandas 的read_csv指定parse_dates[trade_date]让日期列自动转成 datetime省得后面再转一次。df pd.read_csv(data/600519.csv, parse_dates[trade_date], encodingutf-8-sig)3.2 MySQL 建表与批量插入MySQL 适合做多股票联合查询和策略选股。建表时字段类型要卡死trade_date用DATE价格用DECIMAL(10,2)成交量用BIGINTsymbol 用VARCHAR(10)并建索引。主键用(symbol, trade_date)联合主键天然防重复插入。CREATE TABLE stock_daily ( symbol VARCHAR(10) NOT NULL, trade_date DATE NOT NULL, open DECIMAL(10,2), close DECIMAL(10,2), high DECIMAL(10,2), low DECIMAL(10,2), volume BIGINT, amount DECIMAL(20,2), pct_change DECIMAL(10,4), turnover DECIMAL(10,4), PRIMARY KEY (symbol, trade_date), INDEX idx_date (trade_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;utf8mb4别写成utf8虽然后者也能存中文但前者兼容性更好。idx_date索引是给选股查询用的按日期筛全市场股票时走索引快很多。入库用executemany批量插比逐条 insert 快一个数量级。连接用pymysql记得开autocommitFalse每批 commit 一次。import pymysql from sqlalchemy import create_engine def save_to_mysql(df, tablestock_daily): engine create_engine( mysqlpymysql://root:passwordlocalhost:3306/stock?charsetutf8mb4 ) df.to_sql(table, engine, if_existsappend, indexFalse, methodmulti)methodmulti让 pandas 拼批量 insert 语句速度提升明显。if_existsappend配合联合主键重复数据会自动报错想跳过重复就改成先INSERT IGNORE或者入库前先查一遍已有日期。3.3 CSV 与 MySQL 的选型边界什么时候用 CSV什么时候用 MySQL边界很清楚单股票回测、临时分析用 CSV读一个文件几百毫秒全市场选股、多表关联用 MySQL一条 SQL 就能筛出满足条件的股票。两者不是替代关系是互补。我的习惯是 CSV 做原始备份MySQL 做查询层采集完先落 CSV再批量灌 MySQL任何一边坏了都能从另一边恢复。4. K 线图与成交量图渲染方案与常见翻车点4.1 画图库选型matplotlib 还是 pyecharts画 K 线图常见两个选择matplotlib 和 pyecharts。matplotlib 适合静态图、批量出图、嵌入报告pyecharts 适合交互式查看鼠标悬停能看具体数值。做策略验证我倾向 matplotlib因为要批量生成几百张图对比交互式反而拖慢速度。如果只是自己看几只股票pyecharts 体验更好。matplotlib 画 K 线不能直接用plot得用bar或vlines手工拼。更省事的做法是用mplfinance它是 matplotlib 的金融图表封装一行代码出 K 线加成交量。import mplfinance as mpf import pandas as pd df pd.read_csv(data/600519.csv, parse_dates[trade_date], index_coltrade_date) df df.rename(columns{open: Open, high: High, low: Low, close: Close, volume: Volume}) mpf.plot( df.tail(120), typecandle, volumeTrue, stylecharles, title600519 近120日K线, ylabel价格, ylabel_lower成交量, savefigkline_600519.png )typecandle指定蜡烛图volumeTrue在下方叠加成交量style控制配色。tail(120)只画最近 120 个交易日全量画出来太密看不清。savefig直接存图批量跑时文件名带上股票代码和日期。4.2 成交量图的量价配合读法成交量图不是摆设量价配合是判断趋势真伪的核心。常见四种形态价涨量增是健康上涨价涨量缩要警惕背离价跌量增说明抛压重价跌量缩可能是缩量筑底。代码里成交量柱状图默认和 K 线共用 x 轴mplfinance会自动对齐不用手动调。如果想在成交量图上加均线用volume参数传一个 dict指定mavolume周期。比如volume{mavolume: 5}就是五日均量线短线策略常用。4.3 画图环节的三个翻车点第一个翻车点是日期索引没设对。mplfinance要求 DataFrame 的 index 是 DatetimeIndex如果直接传普通列会报TypeError。解决办法就是read_csv时加parse_dates和index_col。第二个是列名大小写。mplfinance认的是Open/High/Low/Close/Volume首字母大写小写会报缺列。重命名这步别偷懒。第三个是中文显示。matplotlib 默认字体不含中文标题会变成方框。加两行配置import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] FalseSimHei是 Windows 自带黑体Linux 下换成WenQuanYi Micro Hei。第二行是让负号正常显示不然涨跌幅为负时坐标轴会出问题。5. 策略预测与选股从指标计算到结果落地的排查清单5.1 均线策略与涨跌预测的实现预测涨跌最简单的起点是均线策略短期均线上穿长期均线看涨下穿看跌。用 pandas 的rolling算均线shift对齐信号避免用到未来数据。def ma_strategy(df: pd.DataFrame, short: int 5, long: int 20) - pd.DataFrame: df df.copy() df[ma_short] df[close].rolling(short).mean() df[ma_long] df[close].rolling(long).mean() # 金叉为 1死叉为 -1其余为 0 df[signal] 0 df.loc[df[ma_short] df[ma_long], signal] 1 df.loc[df[ma_short] df[ma_long], signal] -1 # 信号滞后一天防止未来函数 df[signal] df[signal].shift(1) return dfshift(1)这行是关键不 shift 就是用当天收盘价算的信号当天用回测收益虚高实盘根本做不到。short和long参数按策略周期调短线用 5/20中线用 20/60。5.2 选股逻辑与批量筛选选股就是把上面的信号套到全市场筛出当前 signal 为 1 的股票。用 MySQL 存数据的话可以直接在 SQL 里算均线但 SQL 写 rolling 麻烦我一般还是拉回 pandas 算。def select_stocks(symbols: list, date: str) - list: picked [] for sym in symbols: df pd.read_csv(fdata/{sym}.csv, parse_dates[trade_date]) df ma_strategy(df) latest df[df[trade_date] date].iloc[-1] if latest[signal] 1: picked.append(sym) return pickeddate参数控制选股时点回测时按日期循环调用就能得到每日选股结果。注意iloc[-1]取的是截止 date 的最后一条别直接取全表最后一条否则会用到未来数据。5.3 预测结果每次不一样怎么办热词里有个「模型预测股票涨跌 每次结果不一样」这是高频问题。原因通常三个一是用了随机初始化的模型如神经网络没固定随机种子二是数据顺序没排好rolling 结果不稳定三是特征里混入了未来数据导致每次回测区间不同结果不同。解决办法固定numpy和random的种子数据严格按日期排序特征计算全部用shift对齐。均线策略本身是确定性的只要数据一样结果就一样如果结果飘先查数据顺序。5.4 避坑与排查清单现象一MySQL 报ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock。原因是 MySQL 服务没启动或者 socket 路径不对。解决先systemctl start mysql启动服务再确认my.cnf里的 socket 路径和客户端一致。Windows 下通常是服务没开去服务管理器启动 MySQL 服务即可。现象二CSV 用 Excel 打开中文乱码。原因是编码用了utf-8而不是utf-8-sig。解决存 CSV 时统一加encodingutf-8-sig已经存好的用记事本另存为 UTF-8 with BOM。现象三入库时Incorrect string value报错。原因是表字符集是utf8不是utf8mb4或者连接串没指定 charset。解决建表用utf8mb4连接串加?charsetutf8mb4。现象四K 线图日期错乱、周末也显示。原因是 index 不是 DatetimeIndex或者数据里混入了非交易日。解决read_csv时parse_dates加index_col采集时只保留交易日数据akshare 返回的本身就是交易日不用额外过滤。现象五回测收益高得离谱。九成是未来函数。检查所有指标计算有没有shift信号有没有滞后一天选股时有没有用到当天收盘后的数据。这个坑最隐蔽也最致命。6. 进阶技巧把策略信号落成可复用的选股流水线跑通单只股票只是开始真正省事的是把整条链路封装成一条命令。我的习惯是写一个pipeline.py参数化股票池、日期区间、策略类型跑一次自动完成采集、入库、画图、选股四步。这样每天收盘后跑一遍选股结果直接输出到 CSV第二天开盘前看一眼就行。具体做法是用argparse接参数--mode控制跑哪一步--symbols传股票池文件--date传选股时点。采集和入库做成幂等重复跑不会插重复数据——靠联合主键和INSERT IGNORE兜底。画图只在需要时触发避免每次跑都生成几百张图占磁盘。验证策略有没有效别只看收益曲线。我一般会做三件事一是把信号在 K 线图上标出来肉眼看看买卖点是否合理二是统计信号发出后 1 日、3 日、5 日的平均涨跌幅看是否有统计显著性三是换不同时间段回测如果只有某一段有效大概率是过拟合。这三步走完策略能不能用心里就有数了。还有个细节选股结果别只存代码把当时的均线值、收盘价、成交量一起存下来方便后续复盘。我吃过亏只存了代码过一个月回头看根本想不起当时为什么选它。从那以后我每次跑选股都强制把关键指标一起落表复盘时直接对照省了大量翻数据的时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表