基于机器学习算法的A股股票走势预测系统源码实战:从数据到回测

发布时间:2026/10/12 18:55:03
基于机器学习算法的A股股票走势预测系统源码实战:从数据到回测 简介这份资源是面向金融量化初学者与机器学习实践者的A股走势预测系统源码包围绕数据获取、特征工程、模型训练与回测验证的完整链路展开帮助读者理解如何用算法从历史行情中挖掘价格变动规律。包内共12个文件以6个ipynb交互式笔记为核心配合3个csv行情数据样本、2个py脚本及1个gitignore配置压缩包约2.53MB结构轻量便于快速上手。内容覆盖K线数据入库、小市值结合盈利指标过滤策略验证、FFT滤波、单票LSTM建模与sklearn回测等典型环节涉及线性回归、随机森林、SVM及LSTM等算法的落地思路。目前已有307人学习下载适合希望打通金融数据预处理、模型调参与回测评估全流程的读者参考可据此复现实验并迁移到自己的选股或择时场景中。1. 从一份 A 股日线数据说起机器学习预测股票走势到底靠不靠谱很多人第一次听到「基于机器学习算法实现对A股股票走势预测系统源码」脑子里浮现的是训练一个模型、输入代码、输出涨跌然后坐等收益。我当初也这么想过直到把一份 2018 到 2023 年的全 A 日线数据丢进 LightGBM回测出来的年化还不如沪深300 定投才明白问题不在模型而在「预测什么」和「怎么验证」。这个方向真正能落地的价值不是给你一个稳赚信号而是把「特征工程 标签构造 滚动回测」这套工程链路跑通让你能快速证伪一个想法而不是靠感觉下单。它适合有 Python 基础、懂一点 pandas、想认真做量化研究的从业者不适合指望复制源码就能躺赚的人。下面我按自己实际搭过的一版流程把数据、特征、模型、回测和踩坑讲清楚。2. 数据从哪来、标签怎么打预测系统的地基2.1 A股日线数据的获取与清洗做 A 股预测数据源决定了你后面所有工作的上限。常见做法有三类一是用 akshare、tushare 这类开源接口拉日线二是本地已有券商导出或 Wind/聚宽导出的 CSV三是自己爬东方财富、同花顺。我一般优先用 akshare因为它免费、字段稳定缺点是接口偶尔抽风需要加重试。注意东方财富的页面结构经常变自己写爬虫维护成本高热搜里提到的「东方股吧反爬」就是这个坑非必要不自己爬。下面是我常用的拉取与清洗脚本以 akshare 为例import akshare as ak import pandas as pd import time def fetch_daily(symbol: str, start: str, end: str, retry: int 3) - pd.DataFrame: 拉取单只股票日线带重试 for i in range(retry): try: df ak.stock_zh_a_hist( symbolsymbol, # 6位代码如 000001 perioddaily, start_datestart, # 20180101 end_dateend, adjustqfq # 前复权回测必须用复权价 ) if df.empty: return pd.DataFrame() df df.rename(columns{ 日期: date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: volume, 成交额: amount, 换手率: turnover }) df[date] pd.to_datetime(df[date]) df[symbol] symbol return df.sort_values(date).reset_index(dropTrue) except Exception as e: print(f{symbol} 第{i1}次失败: {e}) time.sleep(2) return pd.DataFrame() # 批量拉取注意加 sleep 避免被限流 symbols [000001, 600519, 300750] all_df [] for s in symbols: d fetch_daily(s, 20180101, 20231231) if not d.empty: all_df.append(d) time.sleep(0.5) data pd.concat(all_df, ignore_indexTrue)逻辑说明adjustqfq是关键参数前复权保证价格连续否则除权日会出现假跳空模型会学到噪声。retry配合sleep是为了应对接口限流实测 0.5 秒间隔比较稳。清洗阶段还要做三件事剔除上市不足 60 个交易日的次新股、剔除停牌日成交量长期为 0、把涨跌停日单独标记因为涨跌停当天特征失真严重。2.2 标签构造预测未来 N 日收益还是涨跌方向标签决定了模型在学什么。常见两种分类标签未来 5 日涨/跌和回归标签未来 5 日收益率。我倾向回归因为分类会丢掉幅度信息而且阈值一改结果全变。标签公式# 未来5日收益率作为回归标签 data data.sort_values([symbol, date]) data[future_ret] data.groupby(symbol)[close].transform( lambda x: x.shift(-5) / x - 1 ) # 二分类标签未来5日是否上涨 data[label] (data[future_ret] 0).astype(int) # 去掉尾部无法计算标签的行 data data.dropna(subset[future_ret])参数说明shift(-5)表示用未来第 5 天收盘价对比今天预测周期 5 日是 A 股短线研究的常用窗口太长噪声大太短交易成本吃光收益。注意标签必须按股票分组计算否则会跨股票串行。这一步最容易翻车的地方是用了未来数据——比如用当日收盘价算标签又用当日收盘价做特征回测漂亮实盘必亏。2.3 特征工程量价、波动率与资金流特征不在多在有没有信息增量。我一般分四组动量类过去 5/10/20 日收益率、波动类20 日收益率标准差、ATR、量能类换手率、量比、成交额环比、位置类收盘价在 20 日高低点中的分位。下面是一个特征生成函数import numpy as np def add_features(df: pd.DataFrame) - pd.DataFrame: df df.sort_values([symbol, date]).copy() g df.groupby(symbol)[close] # 动量 for n in [5, 10, 20]: df[fret_{n}] g.transform(lambda x: x / x.shift(n) - 1) # 波动率 df[vol_20] df.groupby(symbol)[ret_1].transform( lambda x: x.rolling(20).std() ) if ret_1 in df else np.nan df[ret_1] g.transform(lambda x: x.pct_change()) df[vol_20] df.groupby(symbol)[ret_1].transform( lambda x: x.rolling(20).std() ) # 量能 df[vol_ratio] df[volume] / df.groupby(symbol)[volume].transform( lambda x: x.rolling(20).mean() ) # 位置 df[high_20] df.groupby(symbol)[high].transform( lambda x: x.rolling(20).max() ) df[low_20] df.groupby(symbol)[low].transform( lambda x: x.rolling(20).min() ) df[pos_20] (df[close] - df[low_20]) / (df[high_20] - df[low_20] 1e-9) return df data add_features(data)逻辑说明所有滚动计算都必须groupby(symbol)否则会把不同股票的数据混在一起。vol_ratio用 20 日均量做分母衡量当天放量程度。pos_20是 0 到 1 的分位接近 1 说明在近期高位。这些特征都是当日收盘后可知的不存在未来函数。特征做完后要检查缺失值前 20 行必然有 NaN直接 drop 或填充都要按股票分组处理。3. 模型选型与训练LightGBM 为什么比 LSTM 更适合起步3.1 树模型和深度模型的取舍热搜里「机器学习算法」「机器学习模型」「机器学习和深度学习」反复出现很多人一上来就想上 LSTM、Transformer。我的血泪经验是A 股日频数据信噪比极低样本量按股票算也就几千行深度模型很容易过拟合训练还慢。LightGBM 这类梯度提升树对表格特征友好、训练快、可解释性强特征重要性直接告诉你哪些因子在起作用。常见做法是先用 LightGBM 跑通基线再考虑要不要上深度模型。选型对比模型训练速度小样本表现可解释性适合场景LightGBM快好强表格特征、基线XGBoost中好强同上参数更多LSTM慢差弱序列建模、数据量大线性回归极快一般极强因子有效性验证3.2 时序切分与滚动训练股票数据不能随机切分必须按时间切。我一般用「训练 3 年、验证 1 年、测试 1 年」的滚动窗口或者用 walk-forward。下面是最小训练脚本import lightgbm as lgb from sklearn.metrics import mean_squared_error # 按时间切分禁止 shuffle train data[data[date] 2022-01-01] valid data[(data[date] 2022-01-01) (data[date] 2023-01-01)] test data[data[date] 2023-01-01] feat_cols [c for c in data.columns if c.startswith((ret_, vol_, pos_))] params { objective: regression, metric: rmse, learning_rate: 0.05, num_leaves: 31, min_data_in_leaf: 100, # 防止过拟合的关键参数 feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1 } dtrain lgb.Dataset(train[feat_cols], labeltrain[future_ret]) dvalid lgb.Dataset(valid[feat_cols], labelvalid[future_ret]) model lgb.train( params, dtrain, num_boost_round500, valid_sets[dvalid], callbacks[lgb.early_stopping(50)] ) pred model.predict(test[feat_cols]) print(Test RMSE:, mean_squared_error(test[future_ret], pred, squaredFalse))参数说明min_data_in_leaf100是防过拟合最重要的参数A 股噪声大叶子节点样本太少会记住噪声。early_stopping(50)表示验证集 50 轮不提升就停。learning_rate0.05配合 500 轮是比较稳的组合。注意feat_cols的筛选要排除future_ret、label这些标签列否则就是数据泄露。3.3 特征重要性与因子筛选训练完一定要看特征重要性不然你不知道模型在靠什么预测importance pd.DataFrame({ feature: feat_cols, gain: model.feature_importance(gain) }).sort_values(gain, ascendingFalse) print(importance.head(15))如果发现某个特征 gain 异常高先怀疑是不是未来函数。我踩过一次坑把amount成交额和future_ret一起算结果amount排第一查了半天才发现是数据里混入了未来某天的成交额。特征重要性是排查数据泄露的第一道防线。4. 回测验证backtrader 多股回测怎么接预测信号4.1 从预测值到交易信号模型输出的是未来 5 日收益率预测要转成买卖信号。常见做法是设定阈值预测收益大于 1% 买入小于 -1% 卖出中间持有。注意 A 股 T1 和涨跌停限制回测必须模拟。def signal_from_pred(pred_ret: float, buy_th: float 0.01, sell_th: float -0.01) - int: if pred_ret buy_th: return 1 # 买入 elif pred_ret sell_th: return -1 # 卖出 return 0 # 持有参数说明阈值不能拍脑袋要用验证集调。阈值太高信号少太低交易成本吃光收益。我一般会画一张阈值-收益曲线选夏普最高的点。4.2 backtrader 多股回测的最小骨架热搜里「backtrader多股回测」是真实需求单股回测容易过拟合多股才能看出策略稳健性。下面是一个多股回测骨架import backtrader as bt class MLSignalStrategy(bt.Strategy): params ((signal_dict, {}),) # {date: {symbol: signal}} def __init__(self): self.order_dict {} def next(self): dt self.datas[0].datetime.date(0) signals self.params.signal_dict.get(dt, {}) for d in self.datas: sig signals.get(d._name, 0) pos self.getposition(d).size if sig 1 and pos 0: self.buy(datad, size100) elif sig -1 and pos 0: self.close(datad) cerebro bt.Cerebro() cerebro.broker.setcash(1000000) cerebro.broker.setcommission(commission0.0003) # 万三佣金 # 逐个 adddata注意用复权后的数据 # cerebro.addstrategy(MLSignalStrategy, signal_dictsignal_dict) # cerebro.run()逻辑说明signal_dict是提前算好的「日期-股票-信号」映射回测时按日查表。setcommission(0.0003)是佣金别忘了印花税卖出千一A 股实际成本比这高。多股回测要注意资金分配简单等权容易在股票多时每只仓位过小我一般限制同时持仓不超过 10 只。4.3 回测指标怎么看别只看年化收益。我一般看四个夏普比率、最大回撤、胜率、盈亏比。夏普低于 1 基本不用看最大回撤超过 30% 要检查是不是集中持仓。还有一个容易被忽略的换手率。预测周期 5 日、每天调仓换手率会高得吓人实盘成本远超回测。5. 避坑与排查这套系统最容易翻车的 5 个地方5.1 未来函数回测漂亮实盘必亏现象回测年化 50%实盘一上就亏。原因特征里混入了未来信息比如用当日收盘价算了标签又当特征或者用了未来才公布的财务数据。解决所有特征必须用shift(1)确保只用历史数据标签单独算训练前用「特征日期 标签日期」做一次断言检查。5.2 幸存者偏差退市股票没进样本现象模型在历史数据上表现很好但实盘选出的股票经常暴雷。原因拉数据时只拉了当前还在上市的股票退市的没进来样本天然偏向活下来的公司。解决用包含退市股票的完整成分股列表或者至少用指数成分股历史变更数据。akshare 有退市股票接口拉全量。5.3 复权方式选错除权日出现假信号现象某天特征突然异常模型给出错误信号。原因用了不复权价格除权日价格跳空被当成真实下跌。解决回测统一用前复权qfq如果做长周期研究可以用后复权。注意前复权在每次除权后会变历史数据要重新拉。5.4 过拟合验证集调参调到天上去现象验证集夏普 2.0测试集 0.3。原因在验证集上反复调参把验证集也「训练」了。解决留一个从未看过的测试集只在最后跑一次。参数搜索用滚动窗口不要用固定验证集。5.5 交易成本低估换手率吃光收益现象回测年化 20%扣掉手续费和印花税只剩 5%。原因预测周期短、调仓频繁成本被低估。解决回测里把佣金设成万三、印花税千一滑点按 0.1% 算。如果策略换手率超过每年 20 倍基本可以放弃。6. 进阶技巧用滚动训练和集成提升稳健性跑通基线后想再进一步我一般做两件事滚动训练和模型集成。滚动训练是每隔一段时间用最新数据重新训练避免模型老化。下面是一个简化实现def walk_forward(data, feat_cols, train_years3, retrain_freq6M): 滚动训练每6个月重训一次 dates sorted(data[date].unique()) results [] for i, split_date in enumerate(pd.date_range(dates[0], dates[-1], freqretrain_freq)): train_end split_date train_start train_end - pd.DateOffset(yearstrain_years) test_end train_end pd.DateOffset(months6) train data[(data[date] train_start) (data[date] train_end)] test data[(data[date] train_end) (data[date] test_end)] if len(train) 1000 or len(test) 100: continue model lgb.train(params, lgb.Dataset(train[feat_cols], train[future_ret]), num_boost_round300) pred model.predict(test[feat_cols]) results.append({split: split_date, rmse: mean_squared_error( test[future_ret], pred, squaredFalse)}) return pd.DataFrame(results)参数说明train_years3是训练窗口太短学不到规律太长包含过时市场结构。retrain_freq6M是重训频率A 股风格切换快半年一次比较合理。集成则是把 LightGBM、XGBoost、线性回归的预测值做加权平均权重用验证集 IC 确定。实测集成后 IC 能提升 0.01 到 0.02别小看这点在低信噪比场景里很值钱。最后说个习惯我每次改完特征或参数第一件事不是看收益而是看特征重要性和 IC 稳定性。收益可以骗人IC 不会。这套系统值不值得做取决于你愿不愿意花时间在数据和验证上而不是模型本身。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询