【AI+量化实战 #12】从自然语言到可执行策略:LLM生成代码的边界与校验
风险提示:本文仅用于技术演示,不构成投资建议。策略回测结果不代表未来表现,任何代码上线实盘前必须人工复核。
让 AI 帮你写量化策略,是「AI+量化」里最诱人的一步:一句自然语言,几秒就给你一段能跑的 Python。但 AI 生成的代码,你敢直接拿去实盘吗?本系列收官篇,讲清楚「自然语言 → 策略代码」这条链路的边界,以及上线前必须做的校验。
本文你将得到什么
- 一个「自然语言策略描述」的规范模板;
- 一个静态审计器,自动拦截 LLM 代码里的三类高频 bug;
- 一次真实回测:把 LLM 版和人工修正版做对比;
- 「人审 + 回测 + 边界检查」三层校验流程。
一、为什么 LLM 代码不能直接上实盘
LLM 写代码的能力很强,但它在量化场景里最容易犯两类致命错误:
- 字段名写错:接口返回的字段是 close,它写成 Close,一运行就 KeyError;
- 未来函数:算信号时用了 shift(-1)(明天的数据),回测收益虚高,实盘无法复现。
更隐蔽的还有:漏记手续费、忘做信号滞后、把训练集当测试集。所以 LLM 生成的代码,只能当「草稿」,必须过校验闸门。
二、从自然语言到策略:先把需求写清楚
LLM 生成的代码质量,取决于你的描述有多精确。一个规范的策略描述应该包含:
- 信号规则:什么条件买入、什么条件卖出(精确到指标参数);
- 生效时点:信号是当日生效还是次日生效;
- 仓位规则:满仓/空仓,还是分批;
- 成本口径:手续费、滑点怎么算。
示例(双均线择时):
策略:双均线择时。
1. 当 5 日均线上穿 20 日均线(金叉)时买入;
2. 当 5 日均线下穿 20 日均线(死叉)时卖出;
3. 单只股票满仓/空仓切换,不考虑仓位分批;
4. 收益按日频复利计算,买入信号在收盘确认、次日生效。
三、LLM 生成的代码(含典型 bug)
拿到这段描述,LLM 可能生成这样的代码:
def strategy(df):
import pandas as pd
df = df.copy()
df["ma5"] = df["Close"].rolling(5).mean() # bug1: 字段名应是小写 close
df["ma20"] = df["Close"].rolling(20).mean()
df["golden"] = df["ma5"] > df["ma20"]
df["signal"] = df["golden"].astype(int)
df["position"] = df["signal"].shift(-1) # bug2: 未来函数
df["ret"] = df["position"] * df["Close"].pct_change()
return df["ret"].sum() * 100
两处 bug 都很典型:df["Close"] 会直接报错;shift(-1) 让信号偷看了明天。
四、静态审计器:自动拦三类 bug
上线前先跑一遍静态审计,用字符串规则就能拦下大部分问题:
def static_audit(code):
issues = []
# 1) 字段名大小写
for bad in ['["Close"]', '["Open"]', '["High"]', '["Low"]', '["Volume"]']:
if bad in code:
issues.append(f"[P0 字段错误] 检测到 {bad},应为小写 close/open/high/low/volume")
break
# 2) 未来函数 / 前视偏差
if ".shift(-" in code:
issues.append("[P0 前视偏差] 检测到 shift(-N),用到了未来数据")
# 3) 手续费/滑点
if not any(k in code for k in ("fee", "commission", "cost", "slippage")):
issues.append("[P1 缺成本] 未发现手续费/滑点建模,回测收益会被高估")
return issues
对上面的 LLM 代码运行,真实输出:
[P0 字段错误] 检测到 ["Close"],应为小写 close/open/high/low/volume
[P0 前视偏差] 检测到 shift(-N),用到了未来数据
[P1 提示] 仓位/信号应使用 shift(1) 滞后生效,而非 shift(-1) 前视
[P1 缺成本] 未发现手续费/滑点建模,回测收益会被高估
五、回测验证:修正版 vs LLM 版
修好字段、把 shift(-1) 改成 shift(1)(次日生效)、计入双边万 3 手续费后,用真实数据跑一遍双均线策略:
import os
import pandas as pd
import mairui
licence = "LICENCE-66D8-9F96-0C7F0FBCD073" # 演示证书,演示证书(读者复制即用,无需替换)
client = mairui.Client(licence)
rows = client.stock_history(code="600519", period="d",
st="20250801", et="20260814", dividend="n")
df = pd.DataFrame(rows)
df["ma5"] = df["c"].rolling(5).mean()
df["ma20"] = df["c"].rolling(20).mean()
df["signal"] = (df["ma5"] > df["ma20"]).astype(int)
df["position"] = df["signal"].shift(1).fillna(0) # 次日生效
df["daily_ret"] = df["c"].pct_change().fillna(0)
df["cost"] = df["position"].diff().abs().fillna(0) * 0.0003
df["net_ret"] = df["position"] * df["daily_ret"] - df["cost"]
cum = (1 + df["net_ret"]).cumprod()
print(f"总收益 {(cum.iloc[-1]-1)*100:+.2f}%")
真实运行结果(贵州茅台近 172 个交易日):
总收益 -5.50% / 年化 -7.89% / 交易 10 次 / 胜率 42.4% / 最大回撤 -15.19%
结果很诚实:这段双均线策略在这段样本上并不赚钱。这恰恰说明回测的价值——它不负责给你一个「能赚钱」的假象,而是让你看清策略真实表现,避免被 LLM 的「自信代码」骗进实盘。
六、三层校验流程
把上面的步骤固化成一个上线前的流水线:
- 人审(静态审计):字段名、未来函数、缺成本、shift 方向;
- 回测(真实数据):用真实行情跑一遍,看总收益、年化、回撤、胜率,是否与预期一致;
- 边界检查:单日涨跌超限、除权除息、停牌、样本期外表现,逐一过一遍。
三层都通过,才考虑上线;任何一层亮红灯,先回到 LLM 重写或人工修。
七、完整脚本与常见坑
依赖安装:
pip install mairui pandas numpy requests
完整脚本(含 SPEC、LLM 示例代码、静态审计器、参考回测、LLM 评审入口)已整理归档,核心函数:
- load_history():拉日线并统一字段;
- static_audit():静态审计三类 bug;
- reference_strategy():人工修正后的正确实现;
- summarize():回测汇总(总收益/年化/交易次数/胜率/回撤);
- llm_review_prompt():让 LLM 做策略评审。
常见坑:
- 字段名先 probe:接口返回字段先打印出来核对,别让 LLM 猜;
- 信号必须滞后:signal.shift(1) 而不是 shift(-1),否则就是未来函数;
- 成本不能省:万 3 手续费在小样本里可能吃掉大半收益;
- LLM 生成 ≠ 可上线:把它当草稿,人审 + 回测缺一不可。
小结与系列收官
到这里,《AI+量化实战》12 篇全部完成。回顾整个系列,我们反复在讲同一件事:AI 是量化研究的「助手」,不是「股神」——
- 它帮你读公告、比研报、审回测、写解释(#01–#08);
- 它帮你生成日报、判断市场状态(#09–#10);
- 但它的幻觉必须被数据交叉验证、静态体检、回测拦截(#11–#12)。
把「稳定数据 + AI 解读 + 校验闸门」这条链路跑顺,AI 才能真正放大你的研究效率。系列到此收官,感谢阅读。
代码与接口文档参考:https://github.com/MaiRuiApi

浙公网安备 33010602011771号