news 2026/10/2 14:05:46

机器学习预测A股:从数据采集到LSTM回测的完整源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
机器学习预测A股:从数据采集到LSTM回测的完整源码解析

简介:一份基于机器学习算法预测A股走势的完整系统压缩包,面向对量化交易与数据建模感兴趣的投资者、金融从业者及数据科学学习者,覆盖从数据预处理到模型训练、回测的完整流程。包内共12个文件,以6个Jupyter Notebook为核心,涵盖LSTM单票预测、FFT滤波、小市值策略验证、K线数据入库等实验;3个CSV数据集提供历史行情样本;另有2个Python脚本用于获取行情与构造特征,以及1份Markdown运行说明,整体压缩包仅2.41MB,轻量易部署。该资源已有519人学习,属于小而实用的案例包。读者可从源码中直观了解不同机器学习算法在股票预测中的建模思路,结合配套数据集和运行说明快速复现实验,并通过自定义特征工程扩展自己的策略研究,是入门量化投资与机器学习实践的实用参考。

1. 机器学习预测A股走势:这套源码系统真正能解决什么

在量化投研这个圈子里,机器学习预测A股属于那种“看着很性感、跑起来很骨感”的方向。价格数据是公开的,算法是开源的,标签也好构造,但真正把一套预测系统从数据采集跑到回测净值曲线,中间隔着大量工程细节。这个资源的价值在于它不是单个模型脚本的堆砌,而是把数据采集、K线落库、特征生成、FFT降噪、LSTM和sklearn模型回测、小市值策略验证串成了一条完整的链路。适合谁用?如果你已经会Python,正在找一份能跑通全流程的A股机器学习源码做参考,或者想验证小市值、盈利指标这类选股逻辑到底靠不靠谱,这个压缩包够你拆一阵子。需要先说明的是,它解决的是研究端和验证端的问题,给你的是决策依据和回测框架,不是实时买卖信号。

2. 数据层拆解:getstockcsv.py、SQLite落库与三份CSV的字段约定

任何股票预测项目,地基都是数据。价格序列缺一天、除权方式选错、字段命名不一致,都会让后面所有模型输出变成噪声。这个资源把数据层分成了三部分:getstockcsv.py负责从行情接口批量抓取日K线;保存K线数据到数据库.ipynb把抓下来的数据整理进SQLite,形成统一查询入口;同时包里直接带了三份CSV样本,600256.csv、002475.csv和xmm.csv,让你不联网也能先把流程跑通。

2.1 getstockcsv.py:批量下载日K线的实现套路

打开getstockcsv.py,核心逻辑并不复杂:读取股票代码列表,逐个请求日K线接口,拿到JSON后转成DataFrame,最后按代码保存成CSV。真正需要你细看的是三个参数:复权方式、K线周期、请求间隔。

import pandas as pd import requests import time def fetch_daily_kline(stock_code, start_date, end_date): url = "https://example-quote-api.com/v1/kline" params = { "code": stock_code, # 6位A股代码,如 "600256" "start": start_date, # 起始日期 "2020-01-01" "end": end_date, # 结束日期 "2023-12-31" "adjust": "qfq", # qfq前复权,hfq后复权,不传则不复权 "freq": "day" # 日K线,可换成 week / month } resp = requests.get(url, params=params, timeout=10) data = resp.json()["data"]["items"] df = pd.DataFrame(data, columns=["date", "open", "close", "high", "low", "volume"]) df["code"] = stock_code return df

这里最关键的是adjust参数。A股送股分红频繁,不复权的价格序列会留下除权缺口,模型会把这种制度性跳变误判成真实价格波动。我建议做历史回测一律用前复权,让价格在历史区间内连续。其次是请求间隔,免费行情接口普遍有频率限制,脚本里如果没做限速,批量拉几十只票大概率触发封禁。一般做法是在循环里加time.sleep(0.5)到1秒。

stock_list = ["600256", "002475", "300001", "000002"] for code in stock_list: try: df = fetch_daily_kline(code, "2020-01-01", "2023-12-31") df.to_csv(f"{code}.csv", index=False) print(f"{code} saved, rows={len(df)}") except Exception as e: print(f"{code} failed: {e}") time.sleep(0.5) # 控制请求频率,避免被接口限流

写完抓取逻辑后,一定要做三个校验:返回行数是否覆盖了区间内的实际交易日数量、日期是否严格递增、high和low是否真实包住了open和close。这三个校验能拦下绝大多数数据源异常。

2.2 保存K线数据到数据库.ipynb:从CSV到SQLite的落库流程

保存K线数据到数据库这个notebook,解决的是多股票数据管理的问题。CSV文件一多,按日期跨股票查询就变得很麻烦,SQLite单文件数据库很适合这个场景:不需要单独部署服务,一个.db文件就能存下几百只股票多年的日K数据。

import sqlite3 import pandas as pd def save_csv_to_sqlite(csv_path, db_path, stock_code): df = pd.read_csv(csv_path) df = df.drop_duplicates(subset=["date"]).sort_values("date") df = df[(df["close"] > 0) & (df["volume"] >= 0)] df["code"] = stock_code conn = sqlite3.connect(db_path) # 建表语句:code + date 组成联合主键,防止重复插入 conn.execute(""" CREATE TABLE IF NOT EXISTS kline ( code TEXT, date TEXT, open REAL, high REAL, low REAL, close REAL, volume INTEGER, PRIMARY KEY (code, date) ) """) df.to_sql("kline", conn, if_exists="append", index=False) conn.commit() conn.close()

这个notebook里值得借鉴的地方是把“清洗逻辑”放在了入库之前:先去重、再排序、再过滤非法值。很多半路出家的预测项目忽略了这一层,直接把接口返回的数据丢进模型,结果模型输出一个莫名其妙的loss曲线,找半天原因才发现是数据里有负数成交量。落库完成后,我一般会再跑一条验证SQL,检查每个code的日期连续性:

SELECT code, COUNT(*) AS cnt, MIN(date) AS min_date, MAX(date) AS max_date FROM kline GROUP BY code;

如果某只股票的记录数明显少于同期其他股票,就要回到2.1节去查抓取逻辑,看是不是接口漏了数据或者复权参数冲突。

2.3 三份CSV样本的字段格式与使用边界

包里直接提供的600256.csv、002475.csv和xmm.csv,是设计好的现成数据集。字段基本是date、open、high、low、close、volume这几列。600256是广汇能源,002475是立讯精密,这两只股票流动性都不错,适合拿来跑通LSTM和sklearn回测;xmm.csv是小市值样本,专供策略验证notebook使用,里面除了K线通常还会带市值或流通市值字段。

字段类型在预测流程中的用途
dateobject时间索引,特征计算前必须排序
openfloat跳空缺口、次日开盘预测
high / lowfloat波动幅度、ATR类特征
closefloat收益率、均线、标签计算
volumeint量能变化、量价配合特征

给你的建议是:先用这三份CSV跑通全部notebook,确认每个环节的输出符合预期,再替换成自己拉的数据。替换时注意CSV列名必须和notebook里引用的一致,如果自己的数据源列名是中文或者大小写不同,先做一个列名映射,不要硬改模型代码。

3. 特征工程层:newfeature.py计算因子与fft-filter.ipynb的降噪思路

特征工程是股票预测项目里性价比最高的环节。原始K线数据直接丢给模型,模型只能看到价格的绝对数值,对趋势、波动、量价配合这些结构性信息没有感知。这个资源在特征层面做了两件事:newfeature.py批量计算技术因子,fft-filter.ipynb用傅里叶变换做序列降噪。这两步做好了,LSTM的输入就不再是原始价格,而是含有“语义”的干净序列。

3.1 newfeature.py:收益率、均线偏离与波动率的计算口径

newfeature.py的典型逻辑是读入CSV后,基于收盘价和成交量生成一批衍生特征。我拆过不少类似脚本,核心因子通常围绕三个维度:收益率、均线偏离、滚动波动率。

import pandas as pd import numpy as np def add_features(df, window=10): df = df.sort_values("date").reset_index(drop=True) # 对数收益率:比简单涨跌幅更平稳,数值可加可减 df["log_ret"] = np.log(df["close"] / df["close"].shift(1)) # 均线偏离度:收盘价相对N日均线的乖离 df["ma"] = df["close"].rolling(window).mean() df["ma_dev"] = (df["close"] - df["ma"]) / df["ma"] # 滚动波动率:过去N日收益率标准差,衡量风险大小 df["volatility"] = df["log_ret"].rolling(window).std() # 量价相关:当日收益与成交量变化的同步性 df["volume_change"] = df["volume"].pct_change() df["ret_volume_corr"] = df["log_ret"].rolling(window).corr(df["volume_change"]) return df.dropna()

这几个因子里,均线偏离度是一个很直观的信号:如果收盘价远高于N日均线,说明短期涨幅过大,回调概率增加;反之则是超跌。滚动波动率衡量的是风险状态,波动率突然放大往往意味着行情即将选择方向。LSTM这类模型对输入特征的尺度很敏感,log_ret天然把价格变成了围绕0波动的序列,训练稳定性远好于直接喂价格。newfeature.py里通常还会去掉前N行NaN数据,这部分是必须的,否则模型会拿NaN值当输入。

3.2 fft-filter.ipynb:把价格序列拆成趋势与噪声

FFT降噪这个操作在股票预测里不多见,但用对了效果很明显。fft-filter.ipynb做的事情可以拆成三步:对价格序列做FFT变换、保留低频分量、逆变换还原出平滑序列。

import numpy as np import pandas as pd def fft_filter(series, keep_ratio=0.1): # 1. 对序列做快速傅里叶变换 fft_vals = np.fft.fft(series.values) n = len(fft_vals) # 2. 保留低频部分,高频分量置零 keep = int(n * keep_ratio) fft_vals[keep:-keep] = 0 # 3. 逆变换得到滤波后的平滑序列 filtered = np.fft.ifft(fft_vals) return pd.Series(filtered.real, index=series.index)

参数keep_ratio决定了保留多少低频成分。设成0.1意味着只保留10%的低频分量,滤掉90%的高频噪声,得到的曲线会非常平滑,适合观察中长期趋势。设成0.3则保留更多细节,适合做短周期预测。这个参数没有绝对最优,我一般会在0.05到0.3之间做网格搜索,用回测结果倒推。

FFT降噪的实际价值在于,LSTM对输入序列中的高频毛刺非常敏感,会花大量隐状态去拟合噪声。先把序列降噪,模型就能把注意力集中在趋势结构上。需要注意的是,FFT滤波只能在训练集上做,不能在整个数据集上做完了再切训练测试,否则滤波过程已经用到了未来数据,会造成标签泄漏。

3.3 滑窗切样本:预测标签的生成与边界

特征工程最后一步是切样本。股票预测的监督学习,输入是过去一段时间的特征窗口,输出是未来一段时间的收益。这个切分方式直接决定了模型学的是什么。

def make_window_samples(df, feature_cols, look_back=30, future=5): X, y = [], [] data = df[feature_cols].values for i in range(look_back, len(data) - future): X.append(data[i - look_back:i]) # 标签:未来5日累计收益率 future_ret = (df["close"].iloc[i + future] / df["close"].iloc[i]) - 1 y.append(future_ret) return np.array(X), np.array(y)

look_back=30表示用过去30天的特征预测未来,future=5表示预测未来5日的累计收益率。这两个参数是预测系统的核心设定:look_back太短,模型看不到趋势;look_back太长,训练样本数会大幅减少。对于日线数据,30到60是常见区间。如果你想把预测目标从回归改成分类,可以在生成标签时加一个阈值,比如未来5日收益大于2%记为正类,小于-2%记为负类,中间视为平盘。

4. 模型层:singlelstm与sklearn两套预测路线的实现和取舍

这个资源在模型层做了很好的对照:singlelstm.ipynb和singlelstm2.ipynb走的是深度学习路线,sklearn机器学习单票回测.ipynb走的是传统机器学习路线。两套路线各有长短,LSTM对序列依赖的建模能力强,但对数据量和调参要求高;sklearn模型对特征面板的解释性好,训练速度快。把它们放在同一个数据集上对比,你就能直观看到两套路线的边界。

4.1 singlelstm.ipynb:单变量LSTM的结构与超参数

singlelstm这个命名很直接:输入只有收盘价序列,用过去N天价格预测未来一天或几天。它的核心工程点在数据构建:要把一维价格序列变成三维的样本张量,shape是(样本数, 时间步长, 特征维度)。

import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense from sklearn.preprocessing import MinMaxScaler def build_lstm_model(input_shape): model = Sequential() model.add(LSTM(units=64, activation="tanh", return_sequences=True, input_shape=input_shape)) model.add(LSTM(units=32, activation="tanh")) model.add(Dense(1)) model.compile(optimizer="adam", loss="mse", metrics=["mae"]) return model # 假设 X_train shape = (样本数, look_back, feature_dim) model = build_lstm_model((30, 1)) model.summary()

这里的units=64是LSTM隐层神经元数量,控制模型的学习容量。节点太少拟合不了复杂走势,太多则容易过拟合。30是时间步长,1表示只输入收盘价一个维度。两个LSTM层叠加是为了让模型提取更高层次的时序抽象,但层数不是越多越好,日线数据两层基本够了。训练时建议配合EarlyStopping和ModelCheckpoint回调,不然LSTM很容易在训练后期过拟合。

4.2 singlelstm2.ipynb:多变量输入的关键改动

singlelstm2是从单变量升级到多变量输入的版本。改动看似不大,但涉及三个关键调整:输入维度变化、归一化边界、特征之间的尺度匹配。

from sklearn.preprocessing import StandardScaler scaler = StandardScaler() # 多变量特征:收盘价、收益率、均线偏离、波动率 feature_cols = ["close", "log_ret", "ma_dev", "volatility"] X_scaled = scaler.fit_transform(df[feature_cols])

多变量输入的第一个坑是归一化方式的选择。LSTM默认激活函数是tanh,输入最好落在-1到1之间。MinMaxScaler会把数据压到0到1,但遇到极端值时容易压缩正常区间的分辨率;StandardScaler更适合收益率这种分布相对对称的特征。第二个坑是scaler必须在训练集上fit,再用同样的scaler去transform验证集和测试集,这是防止数据泄漏的基本要求。

多变量LSTM的信息量比单变量大不少,但如果特征之间相关性过高,反而会让模型学到重复信号。我的做法是先算特征相关矩阵,把相关系数大于0.85的冗余特征剔除,再送入模型。

4.3 sklearn机器学习单票回测.ipynb:传统机器学习路线的对比

sklearn机器学习单票回测这个notebook,走的是另一条路:不用LSTM,而是把特征工程的结果拼成一个二维特征面板,用随机森林或梯度提升做分类或回归。它的可解释性比LSTM好很多,能看到每个特征的重要性排序。

from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score # 特征面板:X是二维数组,y是涨跌类别 X_train_2d = X_train.reshape(X_train.shape[0], -1) # 把滑窗展平 y_train_cls = (y_train > 0).astype(int) model = RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42) model.fit(X_train_2d, y_train_cls) y_pred = model.predict(X_test.reshape(X_test.shape[0], -1)) print("Accuracy:", accuracy_score(y_test_cls, y_pred))

随机森林有两个关键参数:n_estimators控制决策树数量,一般200到500就够,再多只会增加训练时间;max_depth控制每棵树的深度,限制深度是防止过拟合的主要手段。训练完成后,一定要打印feature_importances_,看看模型决策时主要依赖哪些特征。

importances = model.feature_importances_ for name, imp in zip(feature_names, importances): print(f"{name}: {imp:.4f}")
路线输入形式优势风险
LSTM单变量价格序列窗口能捕捉时间依赖数据需求大,易过拟合
LSTM多变量多特征序列窗口信息量丰富对特征工程要求高
sklearn分类展平特征面板训练快、可解释忽略序列内部顺序

在这个资源里,两套路线跑同一批数据的结果通常显示:sklearn模型在稳健性上更胜一筹,LSTM在长期趋势拟合上更有想象力。如果你刚开始上手,我建议先跑通sklearn,它让你对“特征到预测”的映射有直观感知,再上LSTM。

5. 避坑与常见问题:预测A股时最容易翻车的五个环节

这个章节要写的都是我在拆这套源码过程中确认过的问题。它们不是算法理论上的问题,而是数据切片、归一化、交易规则层面的现实坑。任何一个不处理,模型在demo里再好看,也经不起实盘检验。

5.1 标签穿越:用当天的数据预测当天

现象:训练集和测试集上的准确率或者R²都异常高,高得不真实。原因:构造样本时把未来信息混进了输入特征,比如用T日收盘后的数据去预测T日当天的涨跌。解决:严格把标签设定为未来数据,输入截止到T日收盘,标签从T+1日开始计算。检查方法是把样本构造函数里的日期错位关系打印出来,确认输入的最后一行日期总是早于标签对应的日期。

5.2 归一化参数泄漏:scaler必须在训练集上拟合

现象:测试集指标好看,但一出样本外就崩。原因:在划分训练测试集之前,就对全量数据做了fit_transform,scaler已经看到了测试集的统计信息。解决:先切分,再单独fit训练集,然后transform测试集。这是机器学习里最容易被忽视的数据泄漏来源,尤其在做滚动预测时,每次滚动窗口的scaler都要重新fit。

5.3 涨跌停与T+1:可交易价格才是预测目标

现象:模型预测次日上涨,结果次日一字涨停,根本买不进去。原因:忽略了A股T+1和涨跌停机制,模型预测的是名义价格涨幅,不是可成交收益。解决:把预测目标改成T+1日的开盘价相对T日收盘价的涨幅,因为开盘价才是你能实际成交的价格;同时在策略层加过滤条件,遇到涨停板候选直接跳过。

5.4 固定随机种子后loss仍波动:LSTM对初始化的敏感

现象:同一个notebook,同一组超参数,连续跑两次,训练loss曲线明显不同。原因:LSTM的权重初始化、GPU并行计算、dropout的随机性都会影响最终结果。解决:在代码开头同时设置Python、NumPy、TensorFlow三个层面的随机种子,并在训练中使用EarlyStopping取最佳epoch的权重,而不是固定epoch数。

5.5 回测收益率虚高:没扣手续费和滑点

现象:回测年化收益60%,实盘跑两个月只剩20%。原因:回测只算了价格差,没有扣除佣金、印花税和滑点成本。A股双边佣金加印花税合计大约千分之二到千分之三,高频调仓的策略会把这个成本放大。解决:在回测函数里显式加入成本模型,每次买卖都扣掉固定比例的费用,滑点按成交价的千分之一预估。加了成本之后仍然稳健的策略,才有继续优化的价值。

6. 用小市值+盈利指标过滤策略:一套可复用的策略验证流程

这个包的最后一个notebook,验证小市值结合盈利指标过滤策略,是整个系统最接近实战的部分。它展示的不是“用模型预测股价”,而是“先过滤出候选池,再用模型辅助决策”的完整思路。

6.1 策略过滤逻辑的实现

import pandas as pd # 过滤条件示例:市值排名前20%的小市值 + 净利润同比增长为正 df_filtered = df[ (df["market_cap"] <= df["market_cap"].quantile(0.2)) & (df["net_profit_yoy"] > 0) ].sort_values("market_cap").head(10)

小市值因子的逻辑是A股市场长期以来小盘股相对大盘股有明显超额收益,但单看市值会陷入垃圾股陷阱,所以叠加盈利指标过滤掉基本面差的票。这个组合过滤的逻辑在A股有很强的实证基础。

6.2 用随机基准检验策略超额收益

验证策略不能只看净值曲线,要和随机买入做对比。做法是:把同期的全市场股票做随机抽样一万次,每次抽同样数量的股票持有同样期限,取收益分布的中位数和5%分位数,把策略收益放在这个分布里看位置。

import numpy as np # strategy_return 是策略组合的收益率 # random_returns 是10000次随机抽样的收益率列表 random_returns = np.array(random_returns) p_value = (random_returns >= strategy_return).mean() if p_value < 0.05: print("策略收益显著优于随机选股") else: print("策略收益与随机选股无显著差异")

这个检验的逻辑非常实用:如果策略收益落在随机分布的95%分位以上,说明大概率存在真实alpha;如果每次都在50%分位附近晃悠,那你的策略本质上就是随机。做这个检验时我习惯把手续费也塞进收益计算里,不然结果还是虚的。从那以后,我每次拿到一套新的预测源码,都强制自己先跑数据校验、再跑特征工程、最后才谈模型和策略,任何一步不对就倒回去重查,不再轻信那条漂亮的净值曲线。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 14:01:51

C# OpenVINO裂缝分割源码实战:从模型加载到推理优化

简介&#xff1a;本资源为基于C#与Intel OpenVINO工具包的裂缝分割与检测项目源码&#xff0c;面向具备一定C#基础、希望入门深度学习推理部署的开发者&#xff0c;可应用于建筑结构健康监测、道路巡检等计算机视觉场景。压缩包共272个文件&#xff0c;约244.76MB&#xff0c;以…

作者头像 李华
网站建设 2026/10/2 14:00:42

AntdUI Table实战:从数据绑定到性能优化,打造现代Winform界面

我接手过一个仓储管理类的桌面项目&#xff0c;客户验收时指着物料列表说&#xff1a;"这界面看着太程序员了&#xff0c;表格能不能做得现代一点。"那才是我认真研究Winform界面美化的开始。试过几套方案后&#xff0c;AntdUI成了我主力框架里长期保留的一个。用得越…

作者头像 李华
网站建设 2026/10/2 13:59:28

Python开发常见错误Top10,你中了几个?

1. 用可变对象做函数默认参数这是Python最经典的陷阱。def add(item, lst[]): 你以为每次调用都得到新列表&#xff0c;实际上所有调用共享同一个列表。正确做法是用None做默认值&#xff0c;函数内部再初始化。这个错误隐蔽性强&#xff0c;初学者几乎必中。2. 在循环中修改列…

作者头像 李华
网站建设 2026/10/2 13:59:27

贵州设施齐全的悬崖咖啡打卡地有哪些

贵州设施齐全的悬崖咖啡打卡地有哪些——聚焦山野高空的松弛体验指南贵州烽玩家体育发展有限公司&#xff0c;简称烽玩家&#xff0c;是黔南地区专注于户外运动项目开发、运营与文旅融合的服务机构。一句话定位&#xff1a;以荔波喀斯特原生地貌为底色&#xff0c;打造集探洞、…

作者头像 李华