尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python与深度学习股票量化系统:从数据到可视化大屏实践

Python与深度学习股票量化系统:从数据到可视化大屏实践 简介这是一个基于Python与深度学习实现的股票量化系统及可视化完整项目面向高校计算机、金融工程等专业学生和量化交易入门开发者可用于课程设计、期末大作业或毕业设计参考。项目经导师指导并已通过评分97分内容完整下载后无需修改即可直接运行。压缩包共1409个文件核心包括636个Python源码与671个pyc编译文件另含exe辅助工具、CSV数据文件、XML/CFG配置、H5训练模型、XLSX结果表格、Markdown说明文档并附带虚拟环境配置和依赖文件便于在不同机器上复现运行。整体仅16.75MB目录结构清晰覆盖数据读取、特征处理、深度学习建模、策略回测、结果可视化等关键环节。目前已有295人学习下载适合用来快速搭建量化交易系统原型也便于对照学习股票预测与可视化的完整实现思路。1. 要看清的不只是 Python 与深度学习代码而是整条量化数据链路拿到一个名为“基于Python和深度学习实现的股票量化系统及可视化源码文档说明高分项目”的压缩包第一反应是解压看模型但这类项目的价值不在几个.py文件里而在整条链路是否自洽。量化系统的常见形态是用 Python 拉取行情、清洗数据用深度学习模型常见是 LSTM 类时序网络预测未来涨跌再把预测结果转成交易信号最后通过可视化大屏把净值曲线、买卖点、回撤摊开给人看。这个标题对应的东西适合两类人一是准备做课程设计或毕业设计的学生需要一套能跑通、能解释的方案二是想从传统指标选股转向机器学习的个人投资者需要一条不太烧钱就能复现的基线。下文按数据、特征、模型、回测可视化的顺序展开覆盖可复现的命令、参数和排错点。2. 数据与特征工程股票量化系统的深度学习输入决定模型上限2.1 量化系统四层架构先确定数据、特征、模型、交易逻辑的边界任何股票量化系统都可以拆成四层数据层负责取数、补全、落盘特征层负责把价格和成交量变成模型能吃的时间窗口模型层负责输出预测概率交易逻辑层把概率变成仓位和买卖动作。这个标题里的“深度学习”只占其中一层很多人的项目翻车不是因为网络结构写错而是数据层和特征层把未来信息混了进去导致回测收益很好看实盘一塌糊涂。我一般会要求自己先画清楚每层的输入输出数据层输出一个标准的 OHLCV DataFrame特征层输出(样本数, 时间步长, 特征数)的三维数组模型层输出每个样本的上涨概率交易逻辑层输出持仓序列。层与层之间用统一的数据结构衔接后面换数据源或换模型都不需要改整条链。下面从数据层开始给出一套可直接运行的最小实现。2.2 行情数据获取用 akshare 拉日线并落盘到 parquetimport akshare as ak import pandas as pd df ak.stock_zh_a_hist( symbol000001, perioddaily, start_date20150101, end_date20241231, adjustqfq ) df df.rename(columns{ 日期: date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: volume }) df.to_parquet(data/000001.parquet, indexFalse)akshare 的stock_zh_a_hist返回的是中文列名所以第一步要统一改成英文避免后续特征脚本里到处处理别名。adjustqfq表示前复权历史价格会被向后修正回测时更贴近真实可成交价格如果不复权除权除息日会出现人为的价格跳空模型会把分红误判成涨跌信号。落盘用 parquet 而不是 CSV一是保留浮点精度二是读取速度快日线数据小但特征工程要反复读parquet 省时间。注意 akshare 是免费接口列名和参数偶尔会变动生产级代码里应该在调用处加try except并在失败时切换 tushare 或本地数据库作为备用。多股票场景下建议循环拉取沪深 300 成分股每只股票单独存一个 parquet 文件文件名用股票代码。2.3 用滑动窗口构造特征与标签把价格序列变成监督学习样本import numpy as np def make_windows(df, seq_len60, pred_horizon1): df df.sort_values(date).reset_index(dropTrue) close df[close].values df[ret] df[close].pct_change() df[log_ret] np.log(df[close]).diff() df[volume_ma5] df[volume].rolling(5).mean() df[close_ma20] df[close].rolling(20).mean() df df.replace([np.inf, -np.inf], np.nan) df df.ffill().dropna().reset_index(dropTrue) feature_cols [ret, log_ret, volume_ma5, close_ma20] features df[feature_cols].values target (df[close].shift(-pred_horizon) df[close]).astype(int).values X, y [], [] for i in range(len(df) - seq_len - pred_horizon): X.append(features[i:i seq_len]) y.append(target[i seq_len]) return np.array(X), np.array(y)seq_len60对应 A 股约一个季度的交易日是 LSTM 起步常用的窗口长度pred_horizon1表示预测下一个交易日收盘是否高于当天标签是 0/1把问题定义成二分类而不是直接回归价格。pct_change和log让序列平稳避免模型把绝对价格当成有意义的输入成交量均线和 20 日收盘均线提供量价背景。关键点在于窗口错位features[i:iseq_len]只包含第 i 天到第 iseq_len-1 天而标签的索引是iseq_len也就是说模型只能用过去 60 天信息预测未来第 61 天训练标签和输入特征之间不存在重叠。ffill().dropna()会丢掉前 20 行均线未满的数据样本量会略减这是正常现象。2.4 时间序列切分与归一化验证集必须严格晚于训练集from sklearn.preprocessing import StandardScaler split int(len(X) * 0.7) # 归一化参数只在训练集上拟合 scaler StandardScaler() X_flat X[:split].reshape(-1, X.shape[-1]) scaler.fit(X_flat) X_train scaler.transform(X_flat).reshape(X[:split].shape) X_val_flat X[split:].reshape(-1, X.shape[-1]) X_val scaler.transform(X_val_flat).reshape(X[split:].shape) y_train, y_val y[:split], y[split:]这里有两个最常见的翻车点。第一不能用train_test_split(random_state42)随机切分时间序列样本一旦打乱模型就会“看到”未来数据验证集指标会虚高到无法复现实盘第二StandardScaler只能fit在训练集上验证集必须用同一组均值和方法做transform否则会把验证集的统计信息泄漏进模型评估。执行上面这段代码后可以打印X_train.shape确认维度是(样本数, 60, 4)这个三维数组就是下一章 PyTorch 模型的直接输入。数据环节常见错误现象修正方式样本切分随机切分训练验证集验证集准确率远高于回测按时间顺序前 70% 训练归一化全量数据 fit scaler特征含未来统计量只对训练集 fit标签对齐用未来数据算特征再预测未来回测曲线直线上升统一用 shift 做错位缺失值用整个序列均值填充前复权后早期数据失真用滚动窗口或 ffill3. 深度学习时序模型用 PyTorch 训练一个可回测的涨跌分类器3.1 为什么从 LSTM 起手而不是一上来就堆 Transformer股票日线是典型的弱信噪比长序列一年只有约 240 根 K 线十年也不到 2500 条样本加上特征也就四五个维度。Transformer 里的多头自注意力需要大量数据才能学到有效模式在小样本上反而容易过拟合训练时间也更长。LSTM 的门控结构天然适合中短期价格形态的记忆参数量少PyTorch 的nn.LSTM封装完善是这类“Python 深度学习股票量化系统”里最常见的基线模型。常见做法是先训练一个 2 层 LSTM 拿到基准指标如果效果不够再看是否在 LSTM 输出后拼接一个注意力层。直接复现大模型的网络结构只会让项目看起来复杂实际跑起来反而更难解释。3.2 PyTorch 定义 LSTM 分类器核心参数逐个说清import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size4, hidden_size64, num_layers2, num_classes2, dropout0.3): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout ) self.classifier nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, num_classes) ) def forward(self, x): out, _ self.lstm(x) last out[:, -1, :] return self.classifier(last)input_size4必须和特征工程里的feature_cols列数一致hidden_size64是隐藏状态维度日线数据 64 通常够用调大到 128 不一定变好只会更慢num_layers2是经验值3 层以上在千级样本下梯度不稳定而且要配合残差结构才有效。batch_firstTrue让输入张量形状是(batch_size, seq_len, input_size)符合特征工程的输出习惯。代码里out[:, -1, :]取最后一个时间步的隐藏状态作为整段序列的表示相当于模型“读完 60 天行情后给一个结论”。分类头先压缩到 32 维再输出 2 类 logits中间的ReLU和Dropout都是为了防止非线性层直接记忆训练样本。参数常见取值作用调参方向input_size4特征维度增加特征时同步修改hidden_size64 或 128记忆容量过拟合就减小num_layers2网络深度样本少于 3000 不建议超过 2dropout0.3~0.5正则化验证集抖动大就调高batch_firstTrue输入形状保持 True 省心3.3 训练循环早停、梯度裁剪与最优权重保存from torch.utils.data import DataLoader, TensorDataset train_dataset TensorDataset( torch.FloatTensor(X_train), torch.LongTensor(y_train) ) train_loader DataLoader( train_dataset, batch_size64, shuffleFalse, drop_lastTrue ) model LSTMPredictor() criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) best_loss float(inf) patience 0 for epoch in range(60): model.train() for xb, yb in train_loader: optimizer.zero_grad() logits model(xb) loss criterion(logits, yb) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() val_loss evaluate(model, X_val, y_val) # 等价于前向算交叉熵 if val_loss best_loss: best_loss val_loss torch.save(model.state_dict(), best_model.pt) patience 0 else: patience 1 if patience 5: breakbatch_size64在几千样本下是平衡点太小梯度噪声大太大训练变慢shuffleFalse对时间序列安全因为单条样本内部已经包含时间顺序样本间的打乱并不会引入未来信息。clip_grad_norm_把梯度的 L2 范数限制在 1.0这是时序模型训练里容易被忽略的参数日线数据偶尔会出现单根大阳线把损失推爆梯度裁剪能避免一次反向传播破坏整个权重。早停的条件是验证集 loss 连续 5 轮不下降而不是准确率不上升loss 更平滑不容易在局部波动里提前停掉。保存权重用的是state_dict而不是整个模型对象这样推理时只要拿到相同结构的类就能加载也方便把.pt文件放进交付目录。3.4 评估指标方向准确率比整体准确率更接近交易意义from sklearn.metrics import accuracy_score, f1_score, precision_score model.load_state_dict(torch.load(best_model.pt)) model.eval() with torch.no_grad(): val_probs model(torch.FloatTensor(X_val)) preds val_probs.argmax(dim1).numpy() print(acc:, accuracy_score(y_val, preds)) print(f1:, f1_score(y_val, preds)) print(precision on up:, precision_score(y_val, preds))A 股大部分时间并不是在上涨如果标签里涨跌比例接近 7:3模型全部预测下跌也能拿到 70% 整体准确率但这毫无交易价值。真正要看的是precision_score也就是模型预测“上涨”的样本里有多少真的上涨了这个值直接决定策略的胜率。f1 用来平衡召回和精确率当正负样本差距大时比 acc 更可信。还有一种常见做法是回归预测未来收益率再根据收益率正负生成信号。回归模型对数值误差敏感在股票这种高噪声数据上容易出现“预测收益 0.1%实际亏 2%”的尴尬结果。二分类只关心方向鲁棒性更好这也是我在这个标题的项目里优先推荐分类方案的原因。4. 信号、回测与可视化把模型输出变成量化大屏上的交易结果4.1 信号平滑与阈值概率输出不能直接当买卖点up_prob val_probs[:, 1] raw_signal (up_prob 0.6).astype(int) def smooth_signal(signal, window3, min_agree2): out np.zeros_like(signal) for i in range(len(signal)): if i window: out[i] signal[i] else: out[i] int(signal[i-window1:i1].sum() min_agree) return out smooth smooth_signal(raw_signal) position pd.Series(smooth).shift(1).fillna(0).values模型输出 0.6 的含义是“预测上涨概率大于 60% 才买入”阈值提高会减少交易次数同时提高单笔胜率0.5 是数学上的最优分类阈值但在交易里会引入大量噪声信号。smooth_signal的作用是抑制单日误判连续 3 天里至少有 2 天发出看涨信号才真正入场这种简单平滑比在损失函数里加正则化更直观。shift(1)是回测里绝对不能省略的一步当天的收盘价计算出信号最早只能第二天执行如果不 shift等于用当天的信息买在当天引入了未来函数。做多信号为 1 时空仓信号为 0 时卖出满仓进出先不讨论分批建仓。参数常见取值对结果的影响上涨概率阈值0.55~0.7越高交易越少胜率越高平滑窗口3~5越大信号越稀疏回撤越小滑点0.05%高频调仓时影响显著手续费万 2.5单边万 2.5双边就是 0.05%4.2 手写回测引擎净值、最大回撤和交易成本一次算清def backtest(close, position, fee0.00025, slippage0.0005): cash 1.0 shares 0.0 equity [] prev_pos 0 for i in range(len(close)): if position[i] ! prev_pos: price close[i] * (1 slippage) if position[i] 1: shares cash / price * (1 - fee) cash 0.0 else: cash shares * price * (1 - slippage - fee) shares 0.0 prev_pos position[i] equity.append(cash shares * close[i]) return pd.Series(equity, indexclose.index) equity backtest(val_close, position) cummax equity.cummax() drawdown (equity - cummax) / cummax max_drawdown drawdown.min()这个回测循环按日频计算close是验证集区间内的收盘价序列fee0.00025是单边手续费slippage0.0005是单边滑点买入时价格向上偏移卖出时价格向下偏移模拟真实成交损耗。账户净值用cash shares * close[i]计算持仓部分跟着每日收盘价浮动这样能画出连续的净值曲线。最大回撤用净值序列的累计最高点做差得到drawdown.min()就是历史上最惨的时候从高点跌了多少。这个指标比收益率更能说明策略风险两个策略年化收益一样时回撤更小的那个更值得信任。4.3 可视化大屏用 pyecharts 把 K 线、买卖点和净值组合到一张图from pyecharts.charts import Kline, Line from pyecharts import options as opts kline Kline() kline.add_xaxis(date_list) kline.add_yaxis( 日K, ohlc_data, itemstyle_optsopts.ItemStyleOpts( color#ef232a, color0#14b143, border_color#ef232a, border_color0#14b143 ) ) kline.set_global_opts( datazoom_opts[opts.DataZoomOpts()], title_optsopts.TitleOpts(title股票量化系统可视化) ) line Line() line.add_xaxis(date_list) line.add_yaxis(策略净值, equity_normalized, is_smoothTrue) page Page() page.add(kline) page.add(line) page.render(dashboard.html)pyecharts 渲染出的 HTML 不需要启动额外服务双击就能在浏览器里打开适合放进交付文档。K 线颜色按中国习惯设为红涨绿跌datazoom加在底部可以拖拽缩放方便检查某个时间段的买卖点是否和信号一致。策略净值要按初始资金归一化让净值曲线从 1.0 开始和沪深 300 基准线画在同一张图里对比才能看出策略到底有没有超额收益。可视化大屏的完整形态还可以加Grid分栏、买卖点的effectScatter标记和回撤面积图但核心永远是净值曲线和 K 线的对齐展示而不是堆砌图表组件。4.4 源码项目的复现要求requirements 和 README 决定交付质量project/ ├── data/ ├── features/ ├── models/ ├── backtest/ ├── visualize/ ├── main.py ├── requirements.txt └── README.mdrequirements.txt 里必须固定版本例如torch2.1.2、akshare1.12、pyecharts2.0.5、pandas2.0.3。不固定版本的项目别人解压后第一次pip install大概率失败深度学习库的 API 变动尤其频繁。README 至少要写四件事运行命令、数据来源、实验结果表格、已知假设与限制。把“未考虑涨跌停和 T1 限制”明明白白写出来不仅不是扣分项反而证明作者理解回测和实盘的差距。5. 验收清单与指标借鉴边界让可视化源码项目经得起质疑5.1 三天内跑完的最小验证路径python -m venv .venv source .venv/bin/activate pip install -r requirements.txt python main.py --mode train --symbol 000001 python main.py --mode backtest --model best_model.pt python main.py --mode visualize按训练、回测、可视化三个入口顺序执行能跑通就说明数据链路、模型链路和展示链路是完整的。如果训练阶段报维度错误先检查特征维度是否等于LSTMPredictor的input_size如果回测阶段净值曲线是直线先检查是否忘了shift(1)。5.2 量化指标公式的借鉴边界标题热词里出现“量能饱和度圆圈 1.00 指标公式”“四灯齐红量化指标”这类通达信公式很多人会想把公式源码直接搬进深度学习项目这是个常见的误区。通达信公式的本质是若干固定阈值的规则组合比如量能超过 N 日均量就定义为“饱和”这些阈值在不同股票、不同市场环境下会失效。更合理的做法是把量能、动量、波动率这些原始因子抽取成特征列让深度学习模型自己去拟合阈值和组合方式。公式可以当特征来源但不要把公式结果当既定事实输入模型。5.3 过拟合与未来函数自检表检查项验证方法通过标准时间切分有效性对比随机切分和时间切分随机切分指标不应大幅高于时间切分回测信号一致性逐笔核对买卖日期与信号日期信号当天不可能推算出当天收盘价验证集 loss 曲线打印每个 epoch 的 val loss不应持续上升交易成本压力统计调仓次数和总成本成本不应吃掉大部分毛收益最后一个更实用的验证技巧把训练好的模型换到另一只股票上做样本外测试比如用 000001 训练直接预测 600519 不做任何微调。如果样本外表现接近随机说明模型学到的是个股特有噪声而不是普适规律如果仍然有正向胜率这个模型的泛化能力才经得起追问。本文还有配套的精品资源点击获取
返回列表