Al 帮我写交易策略,三道关决定能不能跑

几个月前我搭了个系统,能让 AI 自己写交易策略------真正的 Python 文件------然后拿真实行情跑回测。

第一版它写了个简单的均线金叉策略。干净、合理,回测还赚了点钱。不错。

第二版它偷偷加了行 import os

这篇讲的是接下来发生的事,以及我搭的三道关,保证答案永远是「想法不错,但不行」。

为什么要让 AI 写代码

前两篇文章讲了怎么把 AI 拦在下单路径外面------确保它不能直接下单。那些讲的是控制 AI 想做什么

这篇不一样。这篇是让 AI 写代码,然后真的跑它。不是从菜单里选策略。不是调几个参数。是从零写一个完整的交易策略,80 到 200 行。

为什么要费这个劲?因为有意义的策略不在内置库里。一个人类 quant 可能花一周把布林带宽度和量能过滤、时间衰减出场拧在一起。AI 几秒就能吐一版。它能探索的形态远比硬编码几个策略多得多。

但给 AI 写代码的能力也带来了新问题。代码可能是危险的------import os、读文件、泄露环境变量。或者代码安全但毫无价值------一个过拟合的噪音机,在一段回测上数字漂亮,换个时间段就崩。

两个独立问题,两套独立方案。沙盒夹住危险的。打分卡夹住垃圾的。

我真正怕的是什么

写代码之前我坐下来列了 AI 写的策略能伤害我的所有方式。四类:

逃逸。 Python 有个著名的技巧:().__class__.__bases__[0].__subclasses__() 能钻进 Python 内部,拿到任意类------文件句柄、网络 socket、子进程。不需要 import,纯运行时手段。

副作用导入。 即使一个看着无害的 import 也会执行那个模块的初始化代码。AI 导入了不该碰的东西,伤害在你反应过来之前就发生了。所以拦截必须在 import 之前,不能之后。

运行时卡死。 一段完全合法、符号干净的代码,照样可以是死循环或内存炸弹。静态扫描看不出来------得真跑起来才知道。

合法但垃圾。 代码完全安全,零危险符号,所有关都过。但它是一个 hopelessly overfit 的破玩意儿------500 根 bar 做了 300 笔交易,样本内数字好看,样本外毫无预测力。这不是安全问题,但它会把你的策略库填满噪音。

关键洞察是:这四种风险作用在四个不同时刻------AI 写完代码后、代码 import 时、代码真正运行时、回测结束后。没有单一检查能全兜住。防御必须分布在整个时间线上。

第一关:跑之前先检查

第一关最简单,运行在 AI 的代码被执行之前。

我写了个扫描器,读 AI 的 Python 文件,在语法层面检查------不执行、不 import,只看结构。像个门口查包的保安。

三张清单:

只允许这些 import。 mathstatisticscollectionsdataclassestypingenumjson。七个模块,全部纯计算,无文件系统、无网络。策略需要的其他一切------行情数据、订单类型、成交事件------由第二关提供。AI 不需要 import 任何系统模块。

这些名字禁止。 evalexeccompileopeninputgetattrsetattrglobalslocals------总共十七个。它们是动态执行、反射、文件 I/O 的原语,任何交易策略都不应该需要。

双下划线属性访问全拦。 那个 __class__.__bases__[0].__subclasses__() 逃逸技巧?挡了。除了五个无害的例外(比如 __init__),所有 __xxx__ 形式的属性都不准碰。一条规则灭掉整条逃逸路径。

真正重要的细节在这里:扫描器拒绝代码时,不只是说「不行」。它返回具体哪一行、什么违规类型、一段人能看懂的理由。AI 知道具体错在哪,所以能改、能重试。它是 linter,不是一堵死墙。

第二关:只给它需要的东西

过了扫描的代码进入第二关。这一步我们要真的加载它------编译 Python、让它能跑。但在一个仔细裁剪过的环境里。

每次加载新的 AI 策略,我们从零搭一个沙盒。沙盒里有什么:

  • 一套裁剪过的 Python 内置函数。abslenrangesumsorted------大约 55 个名字,比 Python 默认的 70 多个少了一截。没有 open,没有 eval,没有 __import__。扫描器已经拦过这些了,但这是第二张网------万一第一关漏了什么。
  • 交易系统的所有符号直接注入:一根 K 线长什么样、怎么下单、买单还是卖单、时钟走到哪了。AI 不需要从任何地方 import 这些------代码醒过来的时候它们已经在那里了。
  • 一个怪东西:__build_class__。这是 Python 的隐藏函数,负责让 class 随便什么: 语法能正常工作。不暴露它代码直接报错。它危险吗?不------它的接口是隐式的,AI 不可能构造出合法的调用。搞清楚哪些「看着吓人」的东西其实是安全的,是沙盒设计里最有趣的部分。

这是纵深防御。扫描器是第一张网。裁剪过的沙盒是第二张。真有东西滑过了第一关,运行时也碰不到危险的内置函数。

第三关:确认它真的是个策略

过了前两关的代码已经被扫描过、安全加载了。但它真的是个交易策略吗?还是 AI 写了个跟交易毫无关系的类?

三道检查:

只有一个策略类吗? 零个继承 Strategy 的类 → 拒。一个文件里多个策略类 → 也拒。一文件一策略,保持简单。

它真的会响应行情吗? 策略需要一个叫 on_bar 的方法------每当一根新的 K 线数据到达时,这个函数被调用。如果 AI 忘了写它,策略就是个不会做任何决策的摆设。拒。

引擎能创建它吗? 回测系统启动策略时,会传几个东西进去:名字、时钟、消息总线、交易标的、时间框架。AI 的代码需要能接收这些。如果它的 __init__ 签名不对,引擎创建不了它。拒------但附带一条消息说具体缺了哪个参数。

三关的顺序不是随便排的:扫描代码(静态)→ 在沙盒里加载 → 确认它是真策略(结构)。每次失败都把原因返回给 AI,所以演化循环是「被拒 → 重写 → 再来」,不是一次失败就终止。

再多一层:放笼子里跑

三道关兜住了很多。兜不住纯计算的死循环。代码干净、符号合法、长得也像个策略------然后跑起来不回来了,吊死整个服务。

所以回测的时候,AI 的策略在一个独立子进程里跑,CPU、内存、时间都设了上限。炸也只炸子进程,主服务不受影响。

而且每个 AI 写的策略都有个陪跑------「买入并持有」------同样的行情、同样的起始资金、同样的费率。如果 AI 的策略连最笨的基准都打不过,就不值得看第二眼。

诚实区。 有一条路径还没装上子进程隔离:live runner------就是那个你睡觉时它在实时行情上自动交易的东西。目前它内联执行策略代码,靠两道人工审批(人 promote 策略 + 人 start 运行)来保证代码可信。live runner 的子进程隔离在待办清单上,还没做。如果你自己部署,这条要知道。

另一个问题:安全不等于有用

一个策略可以过完三道关------完全安全、零危险符号------然后毫无价值。500 根 bar 做了 300 笔交易,测试期回报率好看,市场环境一变就崩。

这叫过拟合,不是安全问题,是质量问题。解决方案是一个不让单一数字忽悠你的打分系统。

打分由四项组成:

  • 夏普比率------收益相对于波动。标准指标。
  • 卡尔玛比率------收益相对于最大回撤。这能抓住那些平均看着不错、但偶尔暴跌的策略。
  • 换手惩罚------交易太频繁扣分。防止 AI 靠刷小额交易来刷分。
  • 回撤否决------如果策略从高点亏了超过 30%,直接出局。一票否决。

为什么不能光看夏普比率?因为 AI 会发现规律。它发现高频刷单能在纸面上撑高夏普,虽然真实交易成本会吃掉所有利润------换手惩罚说不行。它也会发现有些策略平均回报漂亮但有过一次灾难性回撤------回撤否决说直接出局。

各项权重是起点,不是圣旨。等积累足够的真实演化数据再回调。但原则比具体数字重要:不让任何单一指标独揽决策权。

整条链路

markdown 复制代码
AI 写源码
  → 第一关:扫描器查危险模式
     ------被拦了?告诉你错在哪行、什么原因。改了重来。
  → 第二关:在裁剪过的沙盒里加载
  → 第三关:确认它真的是个交易策略
  → 放进独立子进程回测,和买入持有并跑
  → 多维度打分
  → 跑赢基准线 → 进候选池
  → 人审核 → 人批准 → 它开始交易

AI 可以尽情发挥创意------想写什么策略写什么策略。但它写的每一行都穿过三道不在乎它意图的闸门,只在乎结构。最后,一个打分函数决定它值不值得跑。

整条链路上,没有一步说「相信 AI」。

改主意会怎么做

  • 扫描器是一把锁,不是一个定理。 它挡住的是我现在知道的东西。新的逃逸手段会出现,规则需要更新。这不是数学上的安全证明,是一个会迭代变好的实用防御。
  • live runner 需要和回测一样的子进程隔离。 目前靠人工闸门。这是加固清单的 top 1。
  • 打分权重是工程判断。 30% 回撤否决、0.3 的 Calmar 系数、0.10 的换手惩罚------这些数字是合理的起点,不是最优值。真实演化数据会告诉我们对不对。
  • 单一分数会丢信息。 把"安全、盈利、稳定、低换手"压成一个数,一定会有信息损失。未来版本会分维度追踪,不煮成一锅。

宁可现在把缺口摊开,也不假装全链路坚不可摧。如果你在搭类似的东西,同样建议:发布真正承重的,喊出还没做的,不 bluff。


AI 写代码。三道关决定能不能跑。一个打分函数决定值不值得留。

不靠 prompt 工程。不靠「请别 import os」。只靠不在乎 AI 意图的结构性检查------它们只在乎文件里实际写的是什么。


相关推荐
沉默王二2 小时前
IDEA 爽用 Claude Code 的终极方案,太丝滑。
agent·ai编程·claude
TrisighT2 小时前
DevEco Code 写鸿蒙 ArkTS 确实快,但我试了三天后把默认引擎换成了 Cursor
ai编程·harmonyos·cursor
feiyu_gao3 小时前
一个人 + AI:246 commits 做出设计系统 CLI 的故事
前端·ai编程·交互设计
吃颗糖豆搞技术3 小时前
Harness Engineering 深度解析:从"能说"到"能做"的工程跃迁
ai编程
葫芦和十三4 小时前
图解 MongoDB 25|分片架构三件套:mongos、config server 和 shard
后端·mongodb·agent
葫芦和十三10 小时前
图解 MongoDB 26|片键设计:决定集群命运的一个决定
后端·mongodb·agent
Avan_菜菜11 小时前
使用 Docker + rclone 自建 WebDAV
后端·agent·claude
浩风祭月18 小时前
AI 改代码总爱顺手重构?一份 Task Contract 把修改范围锁住
ai编程·claude·cursor