【AI+量化实战 #12】从自然语言到可执行策略:LLM生成代码的边界与校验

风险提示:本文仅用于技术演示,不构成投资建议。策略回测结果不代表未来表现,任何代码上线实盘前必须人工复核。

让 AI 帮你写量化策略,是「AI+量化」里最诱人的一步:一句自然语言,几秒就给你一段能跑的 Python。但 AI 生成的代码,你敢直接拿去实盘吗?本系列收官篇,讲清楚「自然语言 → 策略代码」这条链路的边界,以及上线前必须做的校验。

本文你将得到什么

  • 一个「自然语言策略描述」的规范模板;
  • 一个静态审计器,自动拦截 LLM 代码里的三类高频 bug;
  • 一次真实回测:把 LLM 版和人工修正版做对比;
  • 「人审 + 回测 + 边界检查」三层校验流程。

一、为什么 LLM 代码不能直接上实盘

LLM 写代码的能力很强,但它在量化场景里最容易犯两类致命错误:

  1. 字段名写错:接口返回的字段是 close,它写成 Close,一运行就 KeyError;
  2. 未来函数:算信号时用了 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 的「自信代码」骗进实盘。

六、三层校验流程

把上面的步骤固化成一个上线前的流水线:

  1. 人审(静态审计):字段名、未来函数、缺成本、shift 方向;
  2. 回测(真实数据):用真实行情跑一遍,看总收益、年化、回撤、胜率,是否与预期一致;
  3. 边界检查:单日涨跌超限、除权除息、停牌、样本期外表现,逐一过一遍。

三层都通过,才考虑上线;任何一层亮红灯,先回到 LLM 重写或人工修。

七、完整脚本与常见坑

依赖安装:

pip install mairui pandas numpy requests

完整脚本(含 SPEC、LLM 示例代码、静态审计器、参考回测、LLM 评审入口)已整理归档,核心函数:

  • load_history():拉日线并统一字段;
  • static_audit():静态审计三类 bug;
  • reference_strategy():人工修正后的正确实现;
  • summarize():回测汇总(总收益/年化/交易次数/胜率/回撤);
  • llm_review_prompt():让 LLM 做策略评审。

常见坑:

  1. 字段名先 probe:接口返回字段先打印出来核对,别让 LLM 猜;
  2. 信号必须滞后:signal.shift(1) 而不是 shift(-1),否则就是未来函数;
  3. 成本不能省:万 3 手续费在小样本里可能吃掉大半收益;
  4. LLM 生成 ≠ 可上线:把它当草稿,人审 + 回测缺一不可。

小结与系列收官

到这里,《AI+量化实战》12 篇全部完成。回顾整个系列,我们反复在讲同一件事:AI 是量化研究的「助手」,不是「股神」——

  • 它帮你读公告、比研报、审回测、写解释(#01–#08);
  • 它帮你生成日报、判断市场状态(#09–#10);
  • 但它的幻觉必须被数据交叉验证、静态体检、回测拦截(#11–#12)。

把「稳定数据 + AI 解读 + 校验闸门」这条链路跑顺,AI 才能真正放大你的研究效率。系列到此收官,感谢阅读。


代码与接口文档参考:https://github.com/MaiRuiApi

posted @ 2026-09-14 13:05  量化分析码农  阅读(21)  评论(0)    收藏  举报