书房的空调嗡嗡作响,努力对抗着鹏城3月的闷热。
林晨盯着屏幕上密密麻麻的代码,眼神专注。过去一周,他像蚂蚁搬家一样,从 tushare 和 akshare 上抓取了沪深 300 成分股过去五年的日线数据——开盘价、收盘价、最高价、最低价、成交量。然后用 pandas 清洗、处理,计算出一系列技术指标:移动平均线(mA)、相对强弱指数(RSI)、布林带(bollinger bands)、mAcd……这些指标被拼接成一个个“特征向量”,按时间顺序排列,形成了他模型的“饲料”。
数据准备好了。接下来,是给这些数据找一个能消化它们、并预测未来的“大脑”。
他选择了 LStm(长短期记忆网络)。这是一种特殊的循环神经网络(RNN),擅长处理像股价序列这样具有时间依赖性的数据。普通的 RNN 在传递长期信息时容易“遗忘”,而 LStm 通过精巧的“门”结构(输入门、遗忘门、输出门)控制信息流,能更好地记住序列中的长期模式。这听起来很像是为捕捉股市那些若隐若现的“趋势”和“周期”量身定做的。
“理论上是这样”,林晨揉了揉发酸的眼睛,自言自语。实践是另一回事。
他打开 pytorch,开始搭建模型结构。输入层对应特征维度,接着是两层 LStm 层,用于捕捉不同时间尺度的依赖关系,然后是 dropout 层——随机“丢弃”一部分神经元连接,防止过拟合的常用手段,最后是全连接层输出预测值:下一个交易日的收盘价。
定义损失函数(mean Squared Error,均方误差),选择优化器(Adam),设置学习率、训练轮数(epoch)……
敲下最后一行代码,林晨深吸一口气,点击运行。
风扇的转速明显加快,笔记本电脑的散热口发出轻微的嘶鸣。屏幕上,代表损失值的曲线开始跳动,随着迭代轮数的增加,那条曲线缓缓向下探去。训练集上的损失在下降,这意味着模型正在从历史数据中学习规律。
同时,他也预留了一部分从未见过的数据作为验证集,用来监控模型在“新数据”上的表现,防止它只死记硬背训练数据。
训练持续了将近四十分钟。当进度条走到终点,林晨立刻启动了回测程序。
回测,就是用训练好的模型,在另一段历史数据(测试集)上模拟交易,检验策略盈利能力。他设定了简单的规则:如果模型预测明日收盘价高于今日收盘价一定阈值(比如 0.5%),则模拟买入;如果预测低于今日收盘价一定阈值,则模拟卖出;否则持有。
程序开始运行,一行行日志快速滚动。林晨的心跳也跟着加快。他仿佛能听到虚拟资金在 K 线图上流动的声音。
终于,回测报告生成了。
他的目光第一时间锁定在几个关键数字上:
测试集累计收益率:18.7%
年化收益率:约 32.5%
夏普比率:1.2
最大回撤:-8.3%
林晨的呼吸停滞了一瞬,随即,一股强烈的热流从胸腔直冲头顶。十八个点!超过三十的年化!夏普比率大于 1 意味着风险调整后的收益还算不错,最大回撤控制得也尚可。
成功了?他的第一个 AI 量化策略,在历史回测中,竟然跑出了如此漂亮的曲线?
他猛地从椅子上站起来,在狭小的书房里来回走了几步,拳头不自觉地握紧。几个月来的焦虑、自我怀疑、埋头苦学的孤寂,在这一刻似乎找到了一个宣泄的出口。这不是纸上谈兵,这是代码和数学构建出的、实实在在的“可能性”。他的路,或许真的走对了!
兴奋持续了几分钟。冷静,必须冷静。林晨强迫自己坐回屏幕前,开始仔细审视回测的细节。
他调出了策略的净值曲线图。曲线总体向上,但中间有几段明显的盘整和下跌。他查看了交易信号分布,有些密集区域连续发出买入信号,有些阶段则长时间空仓。他又对比了模型在训练集和验证集上的损失曲线。
看着看着,他眉头渐渐皱起。
验证集上的损失,在训练后期下降得非常缓慢,甚至有轻微抬头的迹象,而训练集损失还在稳步下降。两者之间的差距在拉大。
一个熟悉的术语跳入脑海:过拟合(overfitting)。
他的模型,可能过于“精通”训练数据中的特定噪声和偶然模式了,以至于损害了它在未知数据上的泛化能力。就像一个学生把一本习题册的每一道题甚至标点符号都背得滚瓜烂熟,但遇到稍微变形的考题就可能抓瞎。回测用的测试集虽然也是历史数据,但和训练集时间上相邻,可能存在一定的相似性,掩盖了部分过拟合的问题。如果拿到真正的、未来的行情中去,表现可能会大打折扣。
“果然没那么简单”,林晨低声说,刚才的兴奋迅速褪去,取而代之的是一种混合着警觉和求知欲的冷静。
他重新审视自己的模型和流程。特征工程是否充分?除了技术指标,是否应该加入更多元的数据,比如他之前计划的基本面数据、情绪指标?LStm 的结构是否合理?层数、神经元数量、dropout 比率是否需要调整?训练数据和测试数据的划分方式是否科学?要不要采用更复杂的交叉验证?
问题一个接一个涌现。但这并没有让他沮丧,反而点燃了他更强烈的探索欲。发现问题,正是进步的起点。
他记下几个 immediate 的优化方向:尝试加入更多特征(先从获取到的财报基础数据开始),调整 LStm 的超参数(隐藏层维度、层数),引入更严格的早停(Early Stopping)机制防止过拟合,甚至考虑结合其他模型(比如注意力机制)来提升对关键时间点的捕捉能力。
窗外,天色已近黄昏。鹏城的天空被染成一片橙红。
林晨保存好代码和回测结果,关掉了密密麻麻的编程界面。他没有感到疲惫,反而有种充实的清醒。第一个策略,就像一个粗糙但能运转的原型机,它证明了这条路有矿可挖,同时也清晰地指出了通往富矿路上需要扫除的障碍。
他走到窗边,看着楼下小区里逐渐亮起的灯火和归家的行人。房贷的压力、失业的阴影、家庭的期待,依然像背景音一样存在。但此刻,他心中多了一份沉甸甸的、由代码和逻辑构建出的底气。
这个策略还不完美,甚至可能充满缺陷。但它是从零到一的突破。
下一步,就是在优化之后,用一点真正的资金,去现实市场里试一试水温。不是全部身家,只是一个小额的、他完全能承受损失的实验账户。回测再漂亮也是“纸上富贵”,真正的战场,在下一刻开盘的交易所里。
他回头看了一眼电脑屏幕,黑暗中,它像一块沉默的黑色矿石,里面却可能蕴藏着改变轨迹的能量。
“慢慢来”,他对自己说,“代码可以骗人,但逻辑不会,人更不能”。
真正的挑战,或许才刚刚开始。