最近,社区里有一位用户提出了一个很实际的问题:用网页版 AI 修改策略,需要不断在聊天窗口和 FMZ 编辑器之间复制代码;对话长了,又要重新解释背景。换成 Codex 这样的编程工具,确实可以直接修改本地项目,但 FMZ 上的 K 线标记、日志、收益曲线,还能一起利用起来吗?
可以。把本地项目、FMZ 远程同步和扩展 API 接起来,就能让 AI 在修改代码之后,继续读取策略的运行记录,检查实际行为,再进行下一轮调整。平台图表依然保留,本地也能积累源码、测试和复盘资料。
本文以 Codex 和 JavaScript 策略为例,介绍我们在策略研究中实际采用的协作方式。其他能读写项目文件、执行脚本的 AI 编程工具,也可以沿用这套流程。
感觉好像很多同学不知道还可以这样配合使用,看来有必要写这一篇!以下文中使用的是GPT6,太弱的模型可能不行,尽量用一线模型,那么我们开始讲解。
小提示:>_<! 如果感觉文章内容太长,不想看,直接把这篇文章扔给AI也可以,它会给你解释清楚以及需要怎么操作。
一、先把开发与运行的分工确定下来
整个流程可以这样组织:
text
提出策略思路、约束与验收要求
↓
AI 读取本地项目 → 修改代码 → 执行测试
↓
FMZ 远程同步 → 核对平台源码与参数 → 加载新版本
↓
FMZ 托管者运行策略,产生状态、订单记录与收益数据
↓
扩展 API 取回数据 → 本地复盘 → 下一轮修改
本地项目保存开发过程,FMZ 托管者负责执行策略,FMZ 页面继续提供监控和图表。AI 根据获得的文件、工具权限和运行数据完成具体任务。
日常交易仍应由策略代码中的明确规则执行。本文的 AI 用于开发和复盘,不需要在每笔下单前调用大模型,也不要求把交易所密钥写入聊天记录。
这里有一个重要前提:AI 能分析的范围,取决于我们实际接通了哪些数据和工具。 只有本地源码,它就无法知道昨晚发生了什么;只有一张状态截图,它也无法还原整夜的订单变化。
二、建立一个能持续接手的本地项目
大白话:简单说就是创建一个本地的策略开发目录(文件夹),然后建立一系列工程化的目录和文件,然后让安装好的 Codex 软件接管这个策略开发工作目录。
如果不熟悉折腾Codex CLI,也可以使用各种操作系统的桌面版Codex,也比较好用,以下按 Codex CLI 使用方式讲。
如果已经安装 Node.js 和 npm,可以在终端安装 Codex CLI,然后进入自己的策略目录:
bash
npm install -g @openai/codex
cd "你的策略项目目录"
codex
首次运行按提示登录。Codex 可以读取项目文件、修改代码并执行本地命令,具体操作受当前权限设置约束;安装与登录方式以 Codex CLI 官方文档 为准。
先从 FMZ 下载现有源码,同时导出一份完整策略配置。.js 只包含代码,完整策略导出的 .xml 还包含参数等配置,二者不能互相替代。有关差异,见 FMZ 完整策略导入与导出说明。
一个小项目可以按下面的结构保存:
text
我的策略/
├── AGENTS.md # 给 AI 的项目规则
├── README.md # 当前版本、运行方式和待解决问题
├── 策略设计.md # 信号、仓位、退出、收益统计口径
├── quant.js # 准备同步到 FMZ 的源码
├── 策略完整导出.xml # 参数、交互等平台配置的备份
├── tests/ # 关键计算与订单场景测试
└── reports/ # 运行快照、复盘和版本验证记录
复杂策略可以拆成多个模块,再构建为一个发布文件。此时应固定构建入口,避免本地模块、发布文件和网页源码各改一份。
Codex 支持通过 AGENTS.md 读取项目指引。我们可以把反复强调的要求写进去,例如:
markdown
# 项目规则
- 修改前阅读 README.md、策略设计.md 和最新复盘报告。
- 本地源码是开发依据;若网页发生手工修改,先拉回核对。
- 修改订单逻辑时,检查部分成交、撤单中成交和重复回报。
- 保留收益统计,分别记录已实现、未实现、手续费与资金费。
- 发布前记录测试结果;发布后核对源码与实盘运行版本。
- 密钥通过本机环境变量或独立凭据文件读取,不写入源码和报告。
项目规则的加载方式见 Codex 的 AGENTS.md 文档。这份文件用于指导协作,实际权限仍由工具和接口权限控制。
每轮结束,再让 AI 更新 README:本地是什么版本、平台存的是什么版本、实盘正在跑什么版本,以及下一步还缺什么证据。即使更换会话,也可以从这些文件恢复工作,不必重新叙述全部聊天历史。源码再配合 Git 保存检查点,便于比较和回退。
三、接通 FMZ 的两条通道
重点必看:在实践以下内容之前,先让 Codex Agent 熟读一遍 FMZ 平台的API文档,直接告诉AI先去阅读FMZ平台文档即可
需要接通的通道有两条:一条上传代码,一条读取运行数据。先分清相关凭据:
| 凭据 | 用途 | 在这套流程中的位置 | 大白话解释 |
|---|---|---|---|
| 策略远程同步令牌 | 上传指定策略的源码 | 本地同步脚本 | 赋予AI可以直接修改策略的权限,并且同步到FMZ线上生产环境 |
| FMZ 扩展 API 的 AccessKey / SecretKey | 查询实盘、日志等平台数据;可按权限开放管理接口 | 本地查询脚本 | 赋予AI可以查看实盘运行数据、日志、操作实盘等权限 |
-
策略远程同步令牌(策略同步码),打开你要让AI设计操作的策略页面:
拿到这个同步码,直接给AI就可以了,使用完可以删除这个同步码,也不会有安全问题。
-
FMZ 扩展 API,在页面:
https://www.fmz.com/m/account#apikey。拿到扩展API KEY,直接给AI就可以了,这个用完也可以删除,也可以保存在某个文件,让AI用的时候去读取。
1. 用远程同步上传源码(AI会操作,这里只是了解即可)
在目标策略的编辑页面找到远程同步入口,获取该策略提供的同步命令。以当前项目使用的接口为例,核心操作是向 https://www.fmz.com/rsync 上传源码,并带上该策略的 Bearer 令牌。
下面是 Bash 写法。先通过本机的凭据配置方式设置 FMZ_SYNC_TOKEN 环境变量,上传文件为当前目录下的 quant.js:
bash
# 缺少令牌时立即结束;不要把真实令牌写进这个脚本。
: "${FMZ_SYNC_TOKEN:?请先配置 FMZ_SYNC_TOKEN}"
printf 'Authorization: Bearer %s\n' "$FMZ_SYNC_TOKEN" |
curl --fail-with-body --silent --show-error \
--header @- \
--upload-file quant.js \
https://www.fmz.com/rsync
路径和令牌应以自己策略编辑器生成的命令为准。同步后检查响应内容和目标策略,再回读源码进行比较;不能只看到 HTTP 200 就认定整个发布完成。
源码同步成功,不等于正在运行的实盘已加载新代码。 在本项目的实际同步中,平台源码更新后,原进程仍运行旧版本,需要下一次启动才能加载新版。因此,发布记录要同时保存“已同步版本”和“当前运行版本”,后者以新启动日志及状态栏中的版本为准。
源码同步也不能代替参数迁移。代码新增或修改了参数、交互控件、模板引用时,还要核对平台配置和现有实盘保存的参数值。已有持仓、未决订单和持久化账本如何衔接,应在更新方案中说明。
2. 用扩展 API 读取实盘数据(AI会操作,这里只是了解即可)
在 FMZ 的账户设置中打开“API 接口”,创建扩展 API Key。首次接入可以只开放 GetRobotDetail,GetRobotLogs,分别用于读取实盘详情和运行记录。FMZ 支持按接口名称配置权限,参见 创建 ApiKey 文档。
以下 Python 3 示例只查询实盘详情,不修改策略或启动实盘。运行前,在本机设置 FMZ_ACCESS_KEY、FMZ_SECRET_KEY 和 FMZ_ROBOT_ID 三个环境变量;实盘 ID 不是策略 ID。
python
import hashlib
import json
import os
import time
from urllib.parse import urlencode
from urllib.request import Request, urlopen
params = {
"version": "1.0",
"access_key": os.environ["FMZ_ACCESS_KEY"],
"method": "GetRobotDetail",
"args": json.dumps([int(os.environ["FMZ_ROBOT_ID"])]),
"nonce": str(time.time_ns() // 1_000_000),
}
message = "|".join([
params["version"], params["method"], params["args"],
params["nonce"], os.environ["FMZ_SECRET_KEY"],
])
params["sign"] = hashlib.md5(message.encode("utf-8")).hexdigest()
request = Request(
"https://www.fmz.com/api/v1",
data=urlencode(params).encode("utf-8"),
headers={"Content-Type": "application/x-www-form-urlencoded"},
method="POST",
)
with urlopen(request, timeout=20) as response:
payload = json.load(response)
data = payload.get("data") or {}
if payload.get("code") != 0 or data.get("error") or not data.get("result"):
raise RuntimeError("FMZ 查询失败,请检查响应中的 code 与 data.error")
robot = data["result"]["robot"]
fields = ("id", "status", "strategy_id", "start_time", "refresh", "profit")
print(json.dumps({k: robot.get(k) for k in fields}, ensure_ascii=False, indent=2))
签名拼接方式依据 FMZ 扩展 API 验证文档,返回结构依据 GetRobotDetail 文档。示例保留 HTTPS 证书验证,并检查平台返回的业务错误。
这只是单次查询示例。封装成长期工具时,还要处理超时、限频和错误记录;同一 Key 的请求应统一管理递增的 nonce,避免多个脚本并发时互相冲突。相关要求见 扩展 API 接口详解。
不熟悉接口也没有关系,可以把官方文档和上述结构交给 AI,让它在项目中生成查询脚本。凭据通过本机输入或独立配置注入,AI 调用脚本即可;不要把真实密钥复制到公开文章、策略源码或版本库。
四、FMZ 的图表和收益曲线怎样继续利用
本地编辑改变的是代码的维护位置。策略仍在 FMZ 托管者上运行,原来的 Log、LogStatus、LogProfit、Chart 等输出可以继续使用。它们分别承担运行日志、状态栏、收益曲线和自定义图表的展示,见 FMZ 日志与图表函数说明。
对于 AI 复盘,可以将这些展示对应的数据取回本地:
| 需要检查的内容 | 数据获取方式 | 能回答的问题 |
|---|---|---|
| 实盘状态、启动时间、关联策略 | GetRobotDetail | 实例是否运行,是否重启过,关联是否正确 |
| 下单、撤单、错误及自定义事件 | GetRobotLogs 的策略日志 | 请求是否成功,为什么撤单,有没有接口异常 |
| 收益曲线的记录点 | GetRobotLogs 的收益日志 | 哪段时间净收益发生变化 |
| 自定义图表记录 | GetRobotLogs 的图表日志 | 已记录的价格、指标和标记如何对应 |
| 当前策略状态栏 | GetRobotLogs 返回的 summary | 当前仓位、执行阶段及阻止原因 |
GetRobotLogs 文档说明了日志、收益、图表三组记录以及状态栏字段。查询时按实际需要设置数量,分批读取并按记录 ID 去重;接口返回数据的结构与参数以 GetRobotLogs 官方说明 及实际响应为准。
API 返回的是平台保存的数据,不是整个网页的截图。已有 K 线标记如果记录在图表数据里,可以结合序列定义解析;如果只输出了图片,则需要另外提供图片。策略从未保存的逐笔盘口,也不能靠收益曲线重新推导出来。
因此,图表之外,最好给关键动作留下结构化记录。比如一次订单至少能串起:
text
SUBMIT:提交了什么方向、价格与数量
ACK:交易所是否受理,返回什么订单 ID
FILL:实际成交多少、价格多少、手续费多少
CANCEL:为什么撤单,撤单结果是什么
SETTLED:最终成交量与持仓是否完成核对
这样 AI 才能区分“出现信号”“提交订单”“实际成交”。一条买入日志或订单 ACK,并不等于已经买到;撤单请求发出之后,也可能继续收到部分成交。
状态栏则保持简洁,展示版本、行情和账户新鲜度、仓位、当前订单、主要阻止原因及净收益。详细诊断可以按需输出。下面是嵌入现有主循环的示意片段,diag 由策略自己的统计模块维护:
javascript
// 放入现有交互分发逻辑;已有 GetCommand 调用时不要重复消费命令。
var command = GetCommand();
if (command === "diagnostics") {
Log("DIAGNOSTICS", JSON.stringify({
version: diag.version,
observedAt: Date.now(),
decisions: diag.decisions,
signalPassed: diag.signalPassed,
orderAttempts: diag.orderAttempts,
blockers: diag.blockers
}));
}
这里的 diagnostics 是策略自行实现的命令,不是 FMZ 内置诊断功能。可以配置交互按钮,或在另行开放 CommandRobot 权限后通过接口触发;命令接收机制见 FMZ GetCommand 说明。
收益统计也必须有明确口径。我们的项目将策略净收益拆成已实现、未实现、手续费和带符号的资金费,账户权益另外展示。LogProfit 只负责记录传入的数值,不会自动补全成本,也不会替策略区分充值、其他策略盈亏和自身收益。
为避免刷屏,可以在成交、已对账仓位变化或费用入账等事件后记录收益,状态栏持续更新估值。复盘时同时保存统计区间和估值时间,避免把事件采样曲线当成每秒更新的账户权益曲线。
五、实际案例:为什么策略跑了一夜没有交易
在 Flow Efficiency Maker 的一次迭代中,实盘运行约 15 小时没有新交易。只看当时状态栏,能看到策略在等待信号,却无法判断整夜是否一直如此。
我们通过运行记录、诊断快照和对应版本源码一起检查,得到几个具体事实:
- 约 20.9 万次决策采样中,约 18.9 万次模型数据有效,行情输入并未整夜失效。
- 只有一次采样达到恢复确认;该次候选剩余目标空间约 3.35bp,低于策略要求的 5bp 成本储备。
- 账户、额度和停止机制并不是这次未交易的主要原因。
继续检查代码,发现恢复条件与成本要求存在冲突:策略捕捉约 8bp 起步的价格冲击,却要求先恢复至少 20% 才进入确认,而目标设在恢复 80% 的位置。忽略价格分母和取整的微小差异,可用空间最多约为:
text
8bp × (80% − 20%) = 4.8bp
它已经低于 5bp 的成本储备,后续确认还可能进一步消耗空间。这解释了为什么行情看起来正常、策略也持续计算,却很难形成可执行订单。决策采样之间高度相关,上述次数也不能当成独立交易机会数。
后续调整据此协调恢复时机与成本空间,并补充事件阶段统计,记录机会究竟消失在识别、确认、成本还是执行环节。验证时使用相同的合成行情比较修改前后的行为,再检查平台运行兼容性。
这个案例体现的是协作流程的价值:从实盘证据定位到代码中的具体条件,再用测试验证修改。它并不证明新版已经获得盈利优势;合成场景中的预设成交不能当作市场回测,最终还要观察真实成交后的价格变化、退出成本和净收益。
六、把每轮修改组织成可核对的实验
接入工具以后,不宜只给 AI 一句“继续优化”。一次迭代应当有可判断的目标,例如“查清最近 12 小时没有交易的主要原因”,或者“验证撤单途中部分成交是否重复记账”。
可以直接这样描述任务:
先阅读项目规则、策略设计和最新验证报告,确认本地、平台源码与当前实盘分别是什么版本。通过扩展 API 获取最近 12 小时的日志、收益记录和诊断信息,按时间及记录 ID 保存到 reports。区分信号未触发、金额不足、成本不足、下单失败和未成交撤单,给出数据依据。确认原因后修改本地代码并执行相关测试,记录修改前后的行为差异。源码同步和实盘重启按本轮约定分别执行,并在报告中写明实际完成到哪一步。
这段任务说明把调查对象、证据、改动和发布状态联系了起来。首次接入时可以只做查询,熟悉后再逐步加入已授权的同步和测试操作;扩展 API 有查询与控制接口,不必为了读日志就开放全部权限。
验证也要分层,避免一句“测试通过”覆盖所有问题:
| 验证层次 | 主要检查什么 | 不能据此声称什么 |
|---|---|---|
| 本地计算与场景测试 | 精度、最小订单、订单状态转换、收益记账 | 已验证真实成交质量 |
| FMZ 环境检查 | 代码能否运行,所需函数与数据接口是否兼容 | 已验证交易盈利能力 |
| 历史回放或平台回测 | 在给定数据与撮合假设下的策略表现 | 实盘一定复现结果 |
| 仿真或小额实盘观察 | 实际延迟、成交、撤单、费用和净收益 | 少量交易足以证明长期有效 |
例如,依赖逐笔成交与盘口队列的做市策略,普通 K 线回测无法充分检验排队成交和逆向选择。测试报告需要说明数据粒度、撮合假设、样本区间及未覆盖场景。没有覆盖的问题应保留为待验证项。
每轮交付留下四样东西就很实用:修改后的源码、针对本次问题的测试结果、运行数据与复盘结论,以及明确的发布状态。下一轮 AI 读这些文件,就能继续研究尚未解决的问题。
采用这套方式后,开发者可以把精力更多地放在策略假设、交易约束和证据判断上:让 AI 完成可重复的查询、修改和检查,让 FMZ 承担策略运行与结果记录。每次改动都有对应版本和验证记录,后续才能判断它究竟解决了什么。
顺道分享一个文中实践时的策略:https://www.fmz.com/strategy/550057
- 1





