一台 16G 内存的云服务器,一个想用机器学习炒股的人
先报成绩单:我做了一个 A 股选股模型,验证精度 85%,听起来很唬人吧?
然后我跑了回测,亏了 82%。
这篇文章复盘这个打脸过程的完整技术链路:一个精度指标是怎么骗过我的,以及我后来怎么一步步把「预测得准」修成「真的能赚钱」。所有数字都是我真实回测的输出,代码思路在文末,不构成投资建议。
第一个坑:我的模型根本没在学选股
v1 版本我拍脑袋定的标签是:未来 5 天涨幅超过 6%,记为正样本。LightGBM 跑出来精度 85%,我激动了一晚上。
回测直接给我上了一课。
冷静下来拆解才发现:那段时间大盘本身在涨。「未来 5 天会不会涨」的答案,一大半由大盘决定——牛市里无脑预测 "涨" 就能拿到高精度。我的模型学到的不是「这只股票比别的股票强」,而是「明天大盘涨不涨」。牛市里精度虚高,一转熊全部现形:模型满仓杀进随大盘下跌的股票,回测曲线一路向南。
修复方案:把标签改成相对收益。
正样本 = 未来 20 天内,个股收益 − 沪深 300 收益 ≥ 2%
减掉大盘收益,等于把市场 beta 从标签里剥掉,逼着模型只学「选股 alpha」——它不再能靠「猜大盘」混精度了。
这是整个项目最重要的一次修改。后面所有的正确,都始于这个标签。
第二个坑:随机切分在时间序列上就是作弊
改完标签,精度还是莫名偏高。我又挖了几周,挖出一个更隐蔽的 bug:时间序列泄漏。
我的标签是「未来 20 天」算出来的。而我在用最普通的随机 train_test_split——训练集和验证集在时间上互相穿插。这意味着:训练集最后 20 天的标签里,掺着验证集前 20 天的价格。模型在验证时偷看了未来的答案,精度虚高 5~10 个百分点。
修复方案:封禁切分(embargo)。
train / val / test 之间挖一条 20 天的隔离带(宽度 = 标签周期),隔离带内「标签跨边界」的样本直接丢弃。代价是损失一点数据,换来的是验证集终于干净——精度从「虚高 85%」掉到「诚实 80%」,但这个 80% 是真的能赚钱的。
后来我知道这个思路在专业量化圈有名字(Purged CV 一类),当时是自己被坑出来的。
第三个坑:出场周期必须等于标签周期
标签是 20 天的,但我贪心:拿 20 天训练的信号,5 天就卖,想着「快速复利、多跑几轮」。
回测数据把这条路线证伪得干干净净(同一模型,只改持有期):
| 持有期 | 年化 | 回撤 | 胜率 |
|---|---|---|---|
| 20 天 | +18.6% | -31% | 96% |
| 10 天 | +15.7% | -93% | 88% |
| 7 天 | +11.1% | -95% | 76% |
| 5 天 | +8.2% | -97% | 68% |
年化随持有期单调递增,5 天方案回撤接近归零。原因很直白:alpha 写在 20 天的标签里,你 5 天就离场,等于把还没实现的 alpha 掐死在半路。而「多跑几轮」也是幻觉——实测交易次数几乎没变(1.68 年里 23 次变 25 次),因为真正的瓶颈是信号本身的稀缺,不是周转速度。
标签是多久,就持有多久。标签周期 = 出场周期,这是硬约束。
第四个坑(反直觉):去掉止损,业绩反而变好
风控直觉告诉我必须设止损,我设了 -3%。直到我做压力测试对比:
| 窗口 | 带止损 | 无止损(20天到期) |
|---|---|---|
| 2022 熊市 | 超额 +64.9%,胜率 61% | 超额 +80.0%,胜率 79% |
| 2023-24 阴跌 | 超额 +34.6%,胜率 46% | 超额 +47.2%,胜率 80% |
止损在震荡市里频繁「底部震仓洗出去」——卖在坑里,模型选出的相对强势股随后修复,我已不在车上。
我最终的无止损方案长这样:+5% 止盈;涨 4% 激活移动止盈(高点回撤 4% 卖);20 天到期强制离场。用时间止损替代价格止损,和 20 天标签严格对齐。
当然要说明:这个结论只在我的标签体系下成立,别当成普适真理抄走。
顺便说下工程侧:全 A 股面板是怎么塞进 16G 内存的
这段没有前面精彩,但对想复现的人很实用。全 A 股 11 年日线,构建特征面板后 980 万行 × 180 列,parquet 5GB,pandas 读进来直接膨胀到 35GB——16G 服务器反复 OOM。
不换机器的解法三件套:
- 列过滤读取:
read_parquet只读特征选择后的 117 列,parquet 是列式存储,不读的列根本不碰磁盘,35GB → 8GB - 分块推理:200 万行测试集按 20 万一块逐块预测,峰值内存压到 4GB 以下
- 分层抽样训练:全量跑不动的模型(如 Logistic)按类别比例抽 50 万行训练
结果:980 万行面板从「跑不动」到训练 12~15 分钟、推理不到 1 分钟。单机能解决的事,别上分布式——我当时评估过 Spark,I/O 反而成为新瓶颈。
最终方案和诚实的成绩单
现在的架构一句话:LightGBM + XGBoost + Logistic 三模型独立训练、等渗校准,几何平均共识打分,soft@0.60 阈值出信号,次日开盘等权买入(mp=3 分仓),+5% 止盈 / 移动止盈 / 20 天到期。候选池剔除 ST、退市风险、科创板、北交所。
回测(滚动训练、含 T+1 规则):
| 回测期 | 时长 | 策略 NAV | 沪深300 | 超额 | 年化 |
|---|---|---|---|---|---|
| 主回测(牛+震荡) | 1.68 年 | +31.1% | +25.2% | +5.9% | +17.4% |
| 熊市 2023-24 | 2.00 年 | +48.4% | +1.2% | +47.2% | +21.8% |
| 熊市 2022 | 0.99 年 | +59.0% | -21.1% | +80.1% | +59.7% |
两个必须泼的冷水:
- 实盘要打五折。 回测假设 100% 成交、零滑点。滑点、不全成交、手续费、执行延迟全算上,我的实盘预期是年化 +8~15%,最大回撤 -30~50%。谁跟你吹回测数字不打折,谁就是在骗你——包括骗你自己。
- 2022 那个 +59.7% 是极值,急跌后 V 型反弹吃到的,不可外推。
复盘:精度为什么会骗人
回头看,85% 精度和亏 82% 同时为真,不矛盾——「精度」只衡量预测对不对,不衡量预测对的时候你赚多少、错的时候你亏多少。而这两件事由标签设计、验证方法、出场规则决定,跟模型关系反而最小。
如果只让我留三句话:
- 标签定义了问题:绝对收益标签学的是大盘,相对收益标签才是在学选股
- 验证必须服从时间:时间序列上用随机切分,等于允许模型偷看答案
- 出场周期对齐标签周期:alpha 写在标签里,提前离场就是亲手掐灭它
机器学习那部分(三模型共识、等渗校准)反而是整个项目里最不用动脑子的环节。量化里最难的部分不是建模,是定义「对」。
本文为个人研究复盘,所有数据来自历史回测,不构成任何投资建议。股市有风险,入市需谨慎。