几个月前我搭了个系统,能让 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。 math、statistics、collections、dataclasses、typing、enum、json。七个模块,全部纯计算,无文件系统、无网络。策略需要的其他一切------行情数据、订单类型、成交事件------由第二关提供。AI 不需要 import 任何系统模块。
这些名字禁止。 eval、exec、compile、open、input、getattr、setattr、globals、locals------总共十七个。它们是动态执行、反射、文件 I/O 的原语,任何交易策略都不应该需要。
双下划线属性访问全拦。 那个 __class__.__bases__[0].__subclasses__() 逃逸技巧?挡了。除了五个无害的例外(比如 __init__),所有 __xxx__ 形式的属性都不准碰。一条规则灭掉整条逃逸路径。
真正重要的细节在这里:扫描器拒绝代码时,不只是说「不行」。它返回具体哪一行、什么违规类型、一段人能看懂的理由。AI 知道具体错在哪,所以能改、能重试。它是 linter,不是一堵死墙。
第二关:只给它需要的东西
过了扫描的代码进入第二关。这一步我们要真的加载它------编译 Python、让它能跑。但在一个仔细裁剪过的环境里。
每次加载新的 AI 策略,我们从零搭一个沙盒。沙盒里有什么:
- 一套裁剪过的 Python 内置函数。
abs、len、range、sum、sorted------大约 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 意图的结构性检查------它们只在乎文件里实际写的是什么。
- 📬 Subscribe to Inalpha on Substack ------ 每月一篇长文
- ⭐ github.com/mirror29/in... ------ 三道关、打分系统、全链路都在
services/paper