
简介面向毕业设计、课程设计或入门量化研究的深度学习股票量化交易项目围绕利用CNN、RNN等算法分析历史行情、挖掘股价波动规律并制定交易策略展开。资源共49个文件约2.93MB以Python脚本为主体42个py配以Jupyter Notebook实验、测试配置与readme说明涵盖模型构建、数据加载、特征处理、策略交易及监控等完整环节。项目内置NLinear、DLinear、TFT、TSMixer等多种时序模型以及LightGBM、CatBoost基线对照同时提供行情数据下载、多变量时间序列处理、qmt实盘/模拟盘回调、止损逻辑等落地脚本。代码按models、utils、load_data、strategy、tests等目录组织并配套pytest测试用例结构清晰、复用性强便于理解从数据清洗到模型部署的完整流水线。目前已有91人学习适合希望快速参考完整项目架构、开展二次开发的Python与深度学习爱好者。1. 基于深度学习的股票量化交易为什么值得自己搭一套而不是买现成策略拿到一份“基于深度学习的股票量化交易”代码包很多人第一件事是跑预测、看收益曲线然后被一张漂亮的回测图冲昏头。但真实情况往往是模型在训练集上准确率很高一上实盘就蔫或者回测年化翻倍换一只股票就变成稳定亏损。这个方向真正难的不是堆一个LSTM或Transformer而是把数据、特征、回测这三件事做扎实。这篇文章不聊玄学就按我实际做过的路径拆开讲怎么取数、怎么构造训练样本、怎么训练模型、怎么写一个不骗自己的回测以及最容易翻车的几个坑。适合有Python基础、想用深度学习做A股或美股日线因子验证的从业者。你要的不是跑通代码而是让每一步都有据可查。2. 数据管道从行情到训练样本先把“喂给模型的东西”做对深度学习模型的性能上限绝大多数由数据决定这一点在量化交易里体现得尤其明显。很多代码包里的模型结构并不复杂真正影响结果的是你拿到的行情是否稳定、复权方式是否合理、特征里有没有混进未来信息。所以这一章先把数据管道搭好。2.1 数据源选型日线还是分钟线复权方式怎么定常见的数据源有akshare、tushare、baostock以及针对美股的yfinance。对于个人研究和策略验证我一般推荐akshare或tushare pro的日线数据。免费接口够用但要注意频率限制和字段稳定性——akshare的接口偶尔会改列名建议在代码里做一次rename和类型转换。日线和分钟线的选择要看你预测的周期。做5日以上的持仓日线数据足够做日内高频交易则必须用分钟线但分钟线对数据清洗和存储的要求高一个量级。新手起步我建议只用日线先把特征工程和回测闭环跑通再去碰分钟级数据。复权是另一个高频踩坑点。股票分红送股后价格会产生跳空不复权的数据会让模型以为发生了剧烈波动。前复权会随最新价格不断修正历史价格导致整个时间序列不稳定。所以我一般用后复权adjust参数里对应hfq它把历史价格按分红送股调整后长期保持可比不会因为后续行情变化而改写过去。import akshare as ak import pandas as pd # 拉取贵州茅台日线后复权 df ak.stock_zh_a_hist(symbol600519, perioddaily, start_date20150101, end_date20240101, adjusthfq) # 统一列名后面所有模型代码都基于这套命名 df df.rename(columns{ 日期: date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: volume }) df df[[date, open, close, high, low, volume]].copy() df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 检查缺失和重复 assert not df[date].duplicated().any(), 日期有重复 print(df.head()) print(df.tail())参数说明symbol是股票代码A股直接传类似“600519”的代码perioddaily指定日线adjusthfq表示后复权。接口返回的是字符串类型的日期需要转成datetime再排序。这里加了一个断言防止拿到重复日期因为有些数据源在停牌日不会返回数据正常情况不会重复。交易日历也要留意。A股有节假日和临时停牌数据里会缺一些日期。对日线模型来说缺几天没太大影响只要不把相邻交易日之间的间隔当作相同时间步就好。如果要做序列模型建议保留原始交易日节奏不要强行按自然日填充。2.2 特征工程用ta库生成技术指标避免未来函数原始OHLCV可以直接喂给模型但效果通常不好。价格绝对数值受大盘和个股基数影响大模型很难跨时间段学习。常见做法是生成两类特征一类是收益率、波动率等相对量另一类是RSI、MACD、布林带这类技术指标。技术指标本身不创造新信息但它们把价格变化做了非线性变换相当于给模型提供了更合适的特征尺度。特征工程里最要命的是“未来函数”。所谓未来函数就是在计算某个t时刻的特征时用到了t1甚至更晚的数据。比如你想用“5日均线”作为特征必须用截止到t时刻的收盘价计算不能顺手把整个序列的均线算完再对齐。pandas的rolling默认是右对齐也就是窗口包含当前行这没问题但如果你用了shift(-1)来对齐特征就引入了未来数据。import talib as ta import numpy as np # 基础收益率特征 df[ret_1] df[close].pct_change(1) # 今日相对昨日的收益 df[ret_5] df[close].pct_change(5) # 5日累计收益 df[ma_5] df[close].rolling(5).mean() # 5日均线 df[ma_20] df[close].rolling(20).mean() # 20日均线 df[std_20] df[close].rolling(20).std() # 20日波动率 # 经典技术指标 df[rsi_14] ta.RSI(df[close].values, timeperiod14) df[macd], df[macd_signal], df[macd_hist] ta.MACD( df[close].values, fastperiod12, slowperiod26, signalperiod9) df[bb_upper], df[bb_middle], df[bb_lower] ta.BBANDS( df[close].values, timeperiod20, nbdevup2, nbdevdn2) # 删除前20行NaN数据 df df.dropna().reset_index(dropTrue)逻辑说明pct_change(5)计算的是从t-5到t的累计收益率特征里没有混入未来信息。ta.RSI、ta.MACD、ta.BBANDS这些talib函数只使用传入序列的当前值及其之前的值天然是因果的。要注意rolling和talib都会在序列开头产生NaN所以最后统一dropna()。删掉的只是前20个交易日对整体样本量影响很小。如果你的环境装不上talib可以用pandas手写RSI和MACD效果差别不大。talib在Windows上有时安装麻烦建议直接pip install ta-lib前先安装对应二进制包或者换用pip install pandas-ta。但无论如何特征计算完成后建议打印出特征矩阵的最后几行人工确认一下NaN是否清理干净否则后面训练时会报错或引入隐式截断。2.3 构造标签与样本划分预测涨跌还是收益率标签设计决定了你的策略语义。最常见的做法是把“未来N日收益率是否为正”作为二分类标签。预测单日涨跌噪声太大胜率很难超过55%所以一般预测未来5日或10日。你也可以做回归直接预测未来收益率数值但回归模型的输出分布不稳定交易信号不好定阈值。我一般用未来5日收盘价是否高于当前收盘价作为标签。这样模型学到的是中期趋势而不是隔夜跳空噪声。标签构造时用shift(-5)把未来价格挪到今天这一行再和今天的收盘价比大小。注意这行代码里确实用到了未来数据但它只是标签标签是训练时监督信号不参与特征计算不构成未来函数泄漏。T 20 # 序列长度每个样本包含过去20个交易日 H 5 # 预测周期看未来5日 # 标签未来第5天收盘价高于今天收盘价 1 df[label] (df[close].shift(-H) df[close]).astype(int) feature_cols [c for c in df.columns if c not in [date, label]] X, y [], [] for i in range(T, len(df) - H): X.append(df[feature_cols].iloc[i - T:i].values) y.append(df[label].iloc[i]) X np.array(X) # (样本数, T, len(feature_cols)) y np.array(y) print(f样本数: {X.shape[0]}, 序列长度: {X.shape[1]}, 特征数: {X.shape[2]}) print(f正样本比例: {y.mean():.2f})参数说明T20表示用过去20根日K线预测未来5日H5是预测周期。循环从iT开始因为前20天没有完整的序列历史终止条件是len(df)-H因为标签需要未来5天的数据。构造出的X是三维张量形状为(样本数, 20, 特征数)这就是LSTM标准输入格式。这里有一个容易被忽略的统计问题重叠窗口导致相邻样本高度相关。因为第i个样本和第i1个样本共享19天历史训练集和验证集如果按时间切分边界附近会出现信息重叠。这不是未来函数但会让验证集指标虚高。解决办法是让样本之间隔开H天或者干脆按时间块划分训练集验证集不要随机打乱。后面的训练代码会按时间顺序切分就是为了避免这种信息泄漏。3. 模型选型与训练从MLP到LSTM深度学习在量化里到底学什么数据管道搭好后进入模型环节。很多初学者一上来就堆Transformer但金融数据的信噪比极低模型容量越大越容易过拟合。我的建议是先从简单模型开始做一条基线再逐步加结构。3.1 为什么LSTM适合序列建模MLP做基线股票数据天然是时间序列每个样本里的20个交易日在时间上有先后顺序。MLP如果直接把每个时间步的特征展平成一维向量等于强行丢掉时间顺序关系。LSTM通过门控机制逐时间步扫描输入理论上能捕捉到“连续几天下跌后出现反弹”这类模式。但LSTM不是神药。金融数据本身信噪比低很多所谓规律是噪声。所以我会先跑一个MLP或逻辑回归作为基线。如果LSTM在验证集准确率和策略收益上不能显著超过MLP问题大概率不在模型结构而在特征或标签设计上。这个反向验证过程能帮你省下大量调参时间也能避免在错误的道路上越走越远。Transformer同样可以做序列建模多头注意力能捕捉长距离依赖但在几千个样本的日线数据上它很容易过拟合。实践中LSTM在中等规模金融时序上稳定性更好而且部署时显存占用小、推理速度快。如果你数据量超过10万条再考虑换成Transformer也不迟。3.2 网络结构与PyTorch实现下面是一个标准的双层LSTM分类器。输入形状是(batch, seq_len, features)输出是一个概率值logit。之所以取最后一个时间步的输出是因为我们预测的是未来最后一个时间步聚合了整个序列的信息。如果取平均或最大池化反而会稀释最近几天的行情变化。import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, dropout0.3): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout ) self.fc nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, 1) ) def forward(self, x): out, _ self.lstm(x) # out: (batch, seq_len, hidden_size) last out[:, -1, :] # 最后一个时间步的隐含向量 return self.fc(last).squeeze(-1)参数说明hidden_size64是LSTM隐层维度决定了模型记忆容量太小欠拟合太大过拟合。num_layers2是层数两层LSTM能提取更高层的时间抽象但训练更慢也更容易记住噪声。batch_firstTrue让输入维度顺序更符合直觉。dropout0.3放在LSTM层间和最后全连接层前作用是随机屏蔽一部分神经元逼迫模型学会更鲁棒的特征。注意这里不能使用双向LSTM。双向LSTM每个时间步会同时看到前后两个方向的信息在NLP里是合理的但在时序预测里意味着t时刻会用到t1之后的数据这种结构上的未来泄漏是最难排查的。3.3 训练循环损失函数、优化器、早停与样本划分训练时最关键的两件事样本按时间顺序划分标准化只用训练集统计量。很多人图省事用train_test_split(X, shuffleTrue)这在时序数据里是灾难因为随机打乱会让模型“看到未来”。正确做法是按百分比切分前70%训练中间15%验证最后15%作为模拟的样本外测试。标准化也容易踩坑。如果对整个X计算mean和std再切分训练集测试集测试集的均值方差信息已经混进训练过程。所以必须先切分再只从训练集计算标准化参数然后应用到验证集和测试集。from torch.utils.data import TensorDataset, DataLoader def split_data(X, y, train_ratio0.7, val_ratio0.15): n len(X) idx1 int(n * train_ratio) idx2 int(n * (train_ratio val_ratio)) X_tr, y_tr X[:idx1], y[:idx1] X_val, y_val X[idx1:idx2], y[idx1:idx2] X_te, y_te X[idx2:], y[idx2:] return X_tr, y_tr, X_val, y_val, X_te, y_te X_tr, y_tr, X_val, y_val, X_te, y_te split_data(X, y) # 只从训练集计算均值和标准差 mean X_tr.mean(axis(0, 1), keepdimsTrue) std X_tr.std(axis(0, 1), keepdimsTrue) 1e-8 X_tr (X_tr - mean) / std X_val (X_val - mean) / std X_te (X_te - mean) / std train_ds TensorDataset( torch.tensor(X_tr, dtypetorch.float32), torch.tensor(y_tr, dtypetorch.float32) ) train_dl DataLoader(train_ds, batch_size256, shuffleTrue) model LSTMPredictor(input_sizeX_tr.shape[2]) optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) criterion nn.BCEWithLogitsLoss() best_val_loss float(inf) patience 10 trigger 0 for epoch in range(50): model.train() train_loss_sum 0.0 for xb, yb in train_dl: optimizer.zero_grad() loss criterion(model(xb), yb) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() train_loss_sum loss.item() # 验证 model.eval() with torch.no_grad(): val_logits model(torch.tensor(X_val, dtypetorch.float32)) val_loss criterion(val_logits, torch.tensor(y_val, dtypetorch.float32)).item() if val_loss best_val_loss: best_val_loss val_loss trigger 0 torch.save(model.state_dict(), best_model.pt) else: trigger 1 if trigger patience: print(fEarly stop at epoch {epoch}) break print(fEpoch {epoch}: train_loss{train_loss_sum / len(train_dl):.4f}, val_loss{val_loss:.4f})逻辑说明AdamW在学习率、权重衰减和解耦上比普通Adam更稳定尤其配合Transformer和LSTM。weight_decay1e-4是L2正则抑制过拟合。clip_grad_norm_把梯度范数限制在1.0防止RNN常见的梯度爆炸。早停机制记录最佳验证集loss连续10个epoch不改善就停止然后把保存的权重恢复出来。这比跑满固定epoch要省心的多也是防止过拟合的最后一层保险。训练完成后在测试集上计算AUC和准确率。注意这里不需要再调整任何超参数测试集在你整个调参过程中只能碰一次。如果碰两次它就不再是真正的样本外数据了。3.4 评估指标准确率、AUC和策略收益哪个可信模型训练结束后大多数人第一个看的是准确率。但在类别不平衡时准确率会骗人。比如正样本占60%模型全部预测正类准确率就有60%但策略完全没有区分度。我一般看三个指标AUC、预测为正时的样本内收益、以及最简单但最实用的“方向准确率”——只统计模型预测置信度高于0.6的那些样本看其中多少真的上涨。准确率和AUC只能描述模型的排序能力不能直接代表策略收益。真正决定策略赚不赚钱的是模型在极端行情下的判断尤其是大涨大跌时是否站对方向。所以评估时要把预测结果转成信号然后立刻进入回测环节。只有回测资金曲线才是最终裁决者。4. 回测框架与策略落地别让回测结果骗了你训练模型只完成了一半工作。接下来要写一个尽量尊重交易规则的回测器。很多人直接在预测概率上画收益曲线忽略手续费、滑点、T1限制结果回测收益是真实交易的三倍以上。这个差距就是所谓的“回测美颜滤镜”。4.1 信号生成从概率到仓位模型输出的是logit要先经过sigmoid转成0到1的概率。然后设定买卖阈值。直接用0.5作为买卖线可以但会造成交易频繁换手因为概率经常在0.5附近抖动。我习惯设定一个缓冲区概率高于0.6才买入低于0.4才卖出中间区域保持原仓位不变。这样能降低交易次数也避免在震荡市里来回打脸。import numpy as np def generate_signals(probs, buy_thresh0.6, sell_thresh0.4): 根据预测概率生成交易信号。 返回数组1买入-1卖出0持有不动。 signals np.zeros(len(probs)) position 0 # 0表示空仓1表示持仓 for i, p in enumerate(probs): if position 0 and p buy_thresh: position 1 signals[i] 1 elif position 1 and p sell_thresh: position 0 signals[i] -1 return signals参数说明buy_thresh和sell_thresh构成了死区。阈值越高交易越少但有可能错过一波快速上涨阈值越低交易越多手续费累积越重。我一般在验证集上试几组阈值比如0.55/0.45、0.6/0.4、0.7/0.3观察交易次数和收益曲线的变化。如果阈值稍微调高收益就大幅下降说明模型信号很弱这时要回去改特征或标签而不是继续调阈值。4.2 回测引擎手续费、滑点与收益计算下面这个回测函数是单标的、全仓进出的简化版。你会在真实项目中遇到很多限制但核心框架先立起来后面再加。买入时以收盘价上浮滑点成交卖出时下浮模拟最不利的成交环境。费用单边按0.15%算包含佣金和印花税的粗略值。def backtest(close, signals, fee_rate0.0015, slippage0.001): cash 1.0 # 初始资金归一化为1 pos 0.0 # 持仓市值 equity [] for i in range(len(close)): if signals[i] 1: # 买入以收盘价滑点成交扣手续费 buy_price close[i] * (1 slippage) pos cash * (1 - fee_rate) / buy_price cash 0.0 elif signals[i] -1: # 卖出以收盘价-滑点成交扣手续费 sell_price close[i] * (1 - slippage) cash pos * sell_price * (1 - fee_rate) pos 0.0 equity.append(cash pos * close[i]) equity np.array(equity) total_return equity[-1] / equity[0] - 1 # 最大回撤 peak np.maximum.accumulate(equity) drawdown (equity - peak) / peak max_drawdown drawdown.min() return equity, total_return, max_drawdown逻辑说明买入时用close * (1 slippage)卖出时用close * (1 - slippage)相当于保守估计成交价把滑点成本算进去。equity记录每个交易日的总资产包括现金和持仓市值。最大回撤用np.maximum.accumulate算历史峰值再计算当前值相对峰值的下跌幅度。这个回测没有考虑当日买入不能当日卖出A股T1后续要改进的话需要把信号执行日期往后推一天或者改用次日开盘价成交。手续费和滑点参数是回测里最敏感的调参对象。一般我会做一次敏感性测试把费率从0.1%调到0.3%如果策略收益从正变负说明策略毛收益很薄实盘大概率亏钱。滑点也一样换手越频繁、标的流动性越差滑点越要放大。4.3 参数调仓频率与样本外测试回测时还要匹配持仓周期。如果你的标签是“未来5日上涨”那么比较合理的做法是每5个交易日调一次仓而不是每天根据新预测换手。每天预测每天换仓五个交易日里的信号变化会被滑点和手续费吃掉大部分收益。一种常见落地方式是滚动调仓每周最后一个交易日用模型预测未来一周涨跌然后买入并持有5个交易日。这么做有两个好处一是标签周期和持仓周期一致二是降低交易频率让费用模型压力变小。样本外测试是回测闭环里的最后一道检验。训练集和验证集是你在调参过程中反复看的不能作为最终判断。把模型从未见过的最近一段数据比如最后10%跑一遍信号和回测如果收益和验证集差距很大说明过拟合严重需要回头降低模型复杂度或增加正则化。后面还有更严格的滚动验证方法在那之前先保留一段“金种子”测试集别碰。5. 避坑指南量化交易里最常见的5个翻车现场这一章是整篇文章的血泪经验汇总。以下每个问题我都踩过或者看着身边的人踩过。按“现象 → 原因 → 解决”写方便你对着排查。5.1 现象训练集准确率95%验证集只有51%这是我见过最多的翻车现场。训练集上表现完美一到验证集就打回原形多半不是过拟合而是数据泄漏。常见泄漏点有三个标准化时用了全样本的均值和方差特征计算时不小心用了未来数据标签构造时由于shift方向错误导致同一时刻的特征和标签重叠。原因排查很简单。把训练好的模型在训练集上输出概率随机抽几个样本打印样本序列的最后一根K线日期和对应的标签日期肉眼核对一下。如果特征里混入了未来信息打印出来的日期关系会明显不对。解决方式是重建数据管道标准化严格只用训练集统计量特征统一用rolling和shift(0)计算标签统一用shift(-H)然后重新划分数据集。5.2 现象回测年化50%实盘或样本外直接亏钱回测收益高得吓人但一放到样本外就蔫了。原因通常是你在回测里多次调整了参数比如买卖阈值、滑点、持仓周期直到回测曲线好看为止。这套参数已经在偷偷借鉴未来的信息相当于把测试集写进了训练过程。解决思路是“参数冷冻”把所有超参数的调整限制在训练集和验证集上测试集只能跑一次。如果一定要做参数搜索用嵌套式时间序列验证即外层循环里把数据切分成多个训练/测试块每次在内层训练块上调参在外层测试块上验证。这套流程麻烦但它是避免“回测幻觉”的最有效手段。5.3 现象模型预测准确率60%策略却不赚钱准确率和赚钱之间没有必然关系。模型可能只在波动小的日子里判断正确而那些日子涨跌空间小自然赚不到钱真正的大涨和大跌它全判断错了一次大亏就把前面的小赢吞掉。准确率是等权平均每个样本而收益是按金额加权两者逻辑不同。解决方式是在模型评估阶段同时看策略收益、夏普比率和最大回撤。尤其是最大回撤如果一个策略最大回撤超过30%即使年化收益高也难坚持实盘。对个人投资者来说心理承受能力和资金规模都会限制策略上线。5.4 现象换一只股票或换一段时间就完全失效深度学习模型很容易学到单只股票特有的统计噪音而不是通用的市场规律。比如用茅台的历史数据训练模型可能学到“连续三天小阳线后大概率继续涨”这在某一阶段成立换到另一只股票或另一个行情阶段就失效。对策是尽量做多股票混合训练。把几十只股票的样本拼在一起每只股票在样本里占一块区域模型被迫学习更普适的特征。如果担心不同股票的价格尺度差异记得在标准化时按股票分组处理不能用全市场统一的标准差。至于时间段失效只能靠不断滚动更新模型这是后话。5.5 现象GPU训练时内存不够CPU预测时又慢到怀疑人生日线数据量不大但如果把特征维度做得太高或者把序列长度拉到60天再加上几千只股票内存就会爆。另一个常见问题是基础班学生写训练循环时不做torch.no_grad()在验证集上保留了梯度导致显存持续累积。解决方式有两个方向一是砍数据降低序列长度和特征维度做一次相关性分析保留20到30个有效特征二是改成批量循环不要一次性把所有样本堆进GPU。预测阶段可以考虑把模型转成TorchScript或者ONNXLSTM在CPU上也能跑得很快。如果实在太慢减少层数或hidden_size代价很小。6. 最后一道工序样本外滚动验证与模型上线前的检查清单静态的一次样本外测试只能证明“这一刻没失效”还不能证明策略可以长期使用。这里给出一个更严格的验证方法和一个基本检查清单这是我每次上线前都会走一遍的流程。6.1 滚动前推验证walk-forward analysis把时间序列切成多个窗口比如用前3年训练后6个月测试然后窗口向前移动3个月重新训练重新测试。这样会得到多个独立的样本外结果能直观看到策略在不同市场状态下的表现是否稳定。def walk_forward(X, y, train_len756, test_len126, step_len63): results [] start train_len while start test_len len(X): X_train X[:start] y_train y[:start] X_test X[start:start test_len] y_test y[start:start test_len] # 训练并回测记录指标 metrics train_and_evaluate(X_train, y_train, X_test, y_test) results.append(metrics) start step_len return results参数说明train_len756约等于3年交易日test_len126约等于6个月step_len63约等于3个月滑步。每次训练都只用测试窗口之前的数据保证没有任何未来信息泄漏。滚动验证如果显示收益在多个窗口中有正有负说明策略不稳定如果多数窗口为正且回撤可控才值得考虑实盘。6.2 三个必做的稳健性测试第一改变随机种子重训三次看指标波动。如果收益方差太大说明模型很不稳定需要降低复杂度。第二把交易成本参数放大一倍观察策略是否还能盈利。第三按牛熊市分段回测比如分别测试2018年熊市和2019年牛市如果其中一个区间剧烈亏损至少要知道亏在哪里。6.3 上线前的检查清单从模型到实盘的最后几步上线前我会重新读一遍数据管道代码确认没有未来函数然后拿最新200个交易日做模拟盘每天记录模型的预测概率第二天对比实际涨跌坚持一个月统计胜率。实盘只投入很小比例资金比如总资金的5%因为再好的回测也不能保证未来。每次实盘交易后记录实际滑点和手续费和回测参数做对比偏差超过预期就回头修参数。我早期做这个方向时最大的教训是把90%的时间花在调模型结构上后来才发现数据泄漏和回测成本才是决定盈亏的关键。现在每做一个新策略先跑通数据管道和回测再回头调模型。这个顺序颠倒过来的话损失的时间和金钱都会让人印象深刻。希望帮到你。本文还有配套的精品资源点击获取