对外接口
从AI助手或外部程序操作平台:AI接入(MCP服务)、扩展API接口、交易终端插件。
AI接入
把发明者量化接入Claude Code、Codex、Cursor等AI编程助手(AI agent)后,可以直接用对话完成写策略、回测、创建和管理实盘、查看日志与收益。AI助手通过平台提供的MCP(Model Context Protocol)服务调用这些功能,用的是一把专门为它创建的API KEY,权限由你在授权时决定,随时可以修改或吊销。
一句话接入
在AI助手里输入:
读 https://www.fmz.com/agent/setup.zh-CN.md 然后接入发明者量化
AI助手会按这份说明完成以下步骤,你只需要在浏览器里点一次同意:
- AI助手向平台申请授权,然后给出一个授权链接(形如
https://www.fmz.com/agent/authorize?code=XXXX-XXXX),链接10分钟内有效。 - 在已登录发明者量化的浏览器中打开链接,确认申请者名称和权限后点击同意。可以在页面上取消勾选不想给的权限。
- AI助手取得API KEY,写入自己的MCP配置并连接。之后就可以直接对它说「列出我的实盘」「给这个策略跑一次回测」。
申请时AI助手会用「名字 @ 机器名」(例如Claude Code @ MacBook)作为这把API KEY的名称。同一个名称再次申请并同意时,旧的API KEY会被吊销、换成新的,重复接入不会累积多余的API KEY。
权限
| 权限 | 允许的操作 | 默认 |
|---|---|---|
| read | 查看策略、实盘、托管者、交易所账户列表与详情,读取日志、消息、账户概览(不含任何密钥) | 是 |
| backtest | 发起、查询、停止回测 | 是 |
| write | 保存策略与版本、分组、告警开关,修改已停止实盘的配置 | 是 |
| trade | 创建、启动、停止实盘,给实盘发送交互命令(会扣费并真实下单) | 是 |
| danger | 删除策略、实盘、托管者,公开策略 | 否 |
danger权限默认不授予,只有AI助手明确申请、并在授权页面勾选时才会获得。AI助手调用[trade]或[danger]类工具前,应当先向你确认。
手动配置
不能执行命令的客户端(例如Cherry Studio)可以手动配置:
- 在「账号设置 → API KEY」(
https://www.fmz.com/m/account#apikey)创建API KEY,记下Access Key和Secret Key。 - 在客户端添加一个MCP服务,类型选Streamable HTTP:
- URL:
https://www.fmz.com/api/mcp/<Access Key> - 请求头:
Authorization: Bearer <Secret Key>
- URL:
Secret Key只能放在请求头里,不能写进URL。各客户端的配置示例:
bash
# Claude Code
claude mcp add --transport http fmz "https://www.fmz.com/api/mcp/<Access Key>" --header "Authorization: Bearer <Secret Key>"
json
{"mcpServers": {"fmz": {"url": "https://www.fmz.com/api/mcp/<Access Key>", "headers": {"Authorization": "Bearer <Secret Key>"}}}}
上面的JSON用于Cursor(~/.cursor/mcp.json)等支持Streamable HTTP的客户端;Claude Desktop需要通过npx mcp-remote转接,见接入说明原文。
安装skills(推荐)
skills是给AI助手阅读的平台知识包:平台操作流程、完整的API文档、各编程语言的策略写法、回测和指标。装上之后AI助手写出的策略更准确。在终端执行:
bash
npx skills add fmzquant/skills --global --yes -a claude-code
-a后面填你使用的AI助手名称(claude-code、codex、cursor、gemini-cli等)。也可以直接在GitHub阅读:https://github.com/fmzquant/skills。
可用的工具
连接后AI助手可以使用以下工具,完整说明以AI助手看到的工具列表为准:
| 权限 | 工具 |
|---|---|
| read | ping、get_account_summary、list_exchanges、list_platforms、list_nodes、list_strategies、get_strategy、list_strategy_versions、get_strategy_version、list_robots、get_robot、get_robot_logs、get_robot_profit、get_robot_output、list_messages、list_groups、revoke_my_key |
| backtest | run_backtest、get_backtest、list_backtests、stop_backtest |
| write | check_strategy、save_strategy、save_strategy_version、delete_strategy_version、update_robot、save_group、move_to_group、delete_group、set_robot_alert、set_node_alert、delete_messages |
| trade | create_robot、start_robot、stop_robot、restart_robot、send_robot_command,以及交易终端插件工具plugin_*(查询行情、下单等) |
| danger | delete_strategy、delete_robot、delete_node、publish_strategy |
一个典型的流程:list_platforms和list_nodes了解账户里有哪些交易所账户和托管者;save_strategy保存策略并用check_strategy检查语法;run_backtest和get_backtest回测;确认后create_robot创建实盘,再用get_robot、get_robot_logs观察运行情况。
安全与管理
- 交易所的API KEY不经过AI助手:在网页的「交易所」页面添加,AI助手按编号选择。所有工具的返回结果里都不包含任何密钥。
- 在
https://www.fmz.com/m/account#apikey可以查看AI助手使用的API KEY,修改权限或锁定。权限除了填写上表的权限名,还可以填工具名,用!工具名排除某个工具。 - 不再使用时,让AI助手调用
revoke_my_key吊销它自己的API KEY,或在上面的页面中删除。 stop_robot只停止实盘,不会平仓。- 第一次让AI助手创建实盘时,建议先回测,再用模拟盘或小额资金运行。
常见问题
- 提示没有在线托管者:创建实盘需要至少一个在线的托管者,见「平台基础 → 托管者」。
- 提示回测任务过多:同时运行的回测数量有上限,让AI助手先用
stop_backtest停止不再需要的回测。 - AI助手说没有某个工具,或者调用被拒绝:这把API KEY没有对应的权限,在API KEY页面修改权限后重新连接。
同一把API KEY也可以用于扩展API接口,供脚本和定时任务调用;能用MCP的场景优先使用MCP。
扩展API接口
扩展API接口是平台的HTTP接口(https://www.fmz.com/api/v1),供脚本、定时任务等程序调用平台功能:查询账号、托管者、策略和实盘,创建、重启、停止实盘,向实盘发送交互命令等。
在AI助手(Claude Code、Cursor等)中交互式地操作平台时,优先使用AI接入(MCP服务):工具更全,参数按名称传递,权限可以按类别授予。两者可以使用同一把API KEY。
使用步骤:创建ApiKey,按验证方式发送请求,方法与参数见扩展API接口详解。
创建ApiKey
在账号设置 → API KEY页面(/m/account#apikey)点击「创建新的ApiKey」,得到一对AccessKey和SecretKey。SecretKey代表这把API KEY的全部权限,不要泄露。在该页面也可以修改已有API KEY的权限,或者禁用、删除API KEY。
权限
创建或修改时,在「API权限」输入框中填写逗号分隔的列表:
*:允许全部扩展API接口。- 方法名:只允许列出的方法,例如
GetRobotList,GetRobotDetail,CommandRobot。 !方法名:排除某个方法,通常与*搭配,例如*,!DeleteRobot,!DeleteNode。
同一把API KEY也可以用于AI接入(MCP服务)。MCP工具除了按工具名授权,还可以按权限类别授权:read、backtest、write、trade、danger(含义见AI接入页面),同样支持用!名称排除。权限类别只对MCP工具生效;扩展API接口只认方法名和*。
权限留空时,扩展API接口不限制方法(MCP开放danger以外的全部工具)。建议按用途只授予需要的方法,例如只用于TradingView警报的API KEY只授予CommandRobot。
验证方式
调用扩展API接口时有两种验证方式:
签名验证:用SecretKey对请求参数签名,SecretKey本身不在网络上传输。程序调用应使用这种方式。直接验证:把SecretKey直接放进请求URL,主要用于TradingView等只能填写一个URL的Webhook场景。
签名验证
请求格式
向https://www.fmz.com/api/v1发送POST请求,参数以表单(application/x-www-form-urlencoded)提交。服务端也接受把同样的参数放在URL查询串中的GET请求,但参数会留在各处的访问日志里,推荐使用POST。
| 参数 | 说明 |
|---|---|
| version | 版本号,固定为1.0。 |
| access_key | API KEY的AccessKey。 |
| method | 调用的方法名,例如GetNodeList。 |
| args | 方法参数组成的JSON字符串:按顺序排列的数组(如[]、[123, "ok"]),或按参数名传值的对象(如{"robotId": 123}),见扩展API接口详解。不传时按[]处理。 |
| nonce | 毫秒时间戳。与服务器时间相差不能超过1小时,并且必须大于这把API KEY上一次请求使用的nonce。 |
| sign | 签名,计算方法见下文。 |
请求中不包含SecretKey。
签名方式
按下面的格式拼接字符串,其中args是实际提交的JSON字符串原文:
plaintext
version + "|" + method + "|" + args + "|" + nonce + "|" + secretKey
对拼接结果计算MD5,转换为32位小写十六进制字符串,作为sign的值。
Python示例
python
import hashlib
import json
import time
import urllib.parse
import urllib.request
ACCESS_KEY = '' # API KEY 的 AccessKey
SECRET_KEY = '' # API KEY 的 SecretKey
def api(method, *args, **kwargs):
d = {
'version': '1.0',
'access_key': ACCESS_KEY,
'method': method,
# 位置参数传数组,关键字参数传对象(按参数名传值)
'args': json.dumps(kwargs if kwargs else list(args)),
'nonce': int(time.time() * 1000),
}
s = '%s|%s|%s|%d|%s' % (d['version'], d['method'], d['args'], d['nonce'], SECRET_KEY)
d['sign'] = hashlib.md5(s.encode('utf-8')).hexdigest()
body = urllib.parse.urlencode(d).encode('utf-8')
with urllib.request.urlopen('https://www.fmz.com/api/v1', body, timeout=10) as resp:
return json.loads(resp.read().decode('utf-8'))
print(api('GetNodeList')) # 托管者列表
print(api('GetRobotList', appId='member2')) # 按参数名传值:标签为 member2 的实盘
print(api('CommandRobot', 123, 'ok')) # 向实盘 123 发送交互命令
print(api('GetRobotDetail', 123)) # 实盘 123 的详细信息
Go示例
mylang
package main
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"
)
const (
accessKey = "" // API KEY 的 AccessKey
secretKey = "" // API KEY 的 SecretKey
baseAPI = "https://www.fmz.com/api/v1"
)
var client = &http.Client{Timeout: 10 * time.Second}
func api(method string, args ...interface{}) (string, error) {
if args == nil {
args = []interface{}{}
}
b, err := json.Marshal(args)
if err != nil {
return "", err
}
nonce := strconv.FormatInt(time.Now().UnixMilli(), 10)
sum := md5.Sum([]byte("1.0|" + method + "|" + string(b) + "|" + nonce + "|" + secretKey))
form := url.Values{
"version": {"1.0"},
"access_key": {accessKey},
"method": {method},
"args": {string(b)},
"nonce": {nonce},
"sign": {hex.EncodeToString(sum[:])},
}
resp, err := client.PostForm(baseAPI, form)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func main() {
ret, err := api("GetNodeList")
fmt.Println(ret, err)
// 用新的配置重启实盘 123,settings 字段见「扩展API接口详解」中的实盘配置说明
settings := map[string]interface{}{
"name": "hedge test",
"strategy": 456,
"period": 60,
"node": 789,
"exchanges": []interface{}{
map[string]interface{}{"pid": 1001, "pair": "BTC_USDT"},
},
}
ret, err = api("RestartRobot", 123, settings)
fmt.Println(ret, err)
}
直接验证
直接验证不计算签名,而是把secret_key直接放在请求参数中,因此可以生成一个固定的URL,填到TradingView等只能设置一个URL的Webhook回调里。
**安全提示**:
secret_key写在URL中,会留在浏览器历史、代理和服务器的访问日志、Webhook服务方的配置里,任何拿到这个URL的人都能以这把API KEY的权限调用接口。建议只在CommandRobot的Webhook中使用直接验证,并为它单独创建一把只授权CommandRobot的API KEY(见创建ApiKey);一旦泄露,立即删除这把API KEY。
请求参数为access_key、secret_key、method、args(JSON数组,需要URL编码),不需要version、nonce、sign。CommandRobot不做nonce校验;其他方法仍会校验:不传nonce时服务器以当前时间(精确到秒)代替,同一秒内的第二次调用会返回Nonce错误(code为3)。
例如API KEY的AccessKey为xxx、SecretKey为yyy,访问下面的URL即可向Id为186515的实盘发送交互命令ok12345:
plaintext
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyy&method=CommandRobot&args=%5B186515%2C%22ok12345%22%5D
接收Webhook请求体
CommandRobot的命令参数为空字符串、请求为POST时,服务器把请求体(Body)作为交互命令发给实盘。例如在TradingView的Webhook URL中设置:
plaintext
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyy&method=CommandRobot&args=%5B186515%2C+%22%22%5D
其中args的值%5B186515%2C+%22%22%5D解码后为[186515, ""](+是URL编码中的空格),186515是实盘Id,命令为空字符串。
模拟TradingView发送Webhook警报:
javascript
function main() {
var options = {
method: "POST",
body: `{"test": 123}`,
headers: {"Content-Type": "application/json"}
}
// Webhook 警报会自动发送 POST 请求,并带上需要的 headers
return HttpQuery("https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyy&method=CommandRobot&args=%5B186515%2C+%22%22%5D", options)
}
TradingView警报消息框中的内容就是请求体:
-
JSON格式:
plaintext{"close": {{close}}, "name": "aaa"}Id为
186515的实盘收到交互命令:{"close": 39773.75, "name": "aaa"}。 -
文本格式:
plaintextBTCUSDTPERP 穿过(Crossing) 39700.00 close: {{close}}Id为
186515的实盘收到交互命令:BTCUSDTPERP 穿过(Crossing) 39700.00 close: 39739.4。
Python、Go示例
python
import json
import urllib.parse
import urllib.request
ACCESS_KEY = '' # 只授权了 CommandRobot 的 API KEY 的 AccessKey
SECRET_KEY = '' # SecretKey
def api(method, *args):
query = urllib.parse.urlencode({
'access_key': ACCESS_KEY,
'secret_key': SECRET_KEY,
'method': method,
'args': json.dumps(list(args)),
})
with urllib.request.urlopen('https://www.fmz.com/api/v1?' + query, timeout=10) as resp:
return json.loads(resp.read().decode('utf-8'))
# API KEY 没有该方法的权限时返回 {'code': 4, 'data': None}
print(api('CommandRobot', 186515, 'ok12345'))
mylang
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
const (
accessKey = "" // 只授权了 CommandRobot 的 API KEY 的 AccessKey
secretKey = "" // SecretKey
baseAPI = "https://www.fmz.com/api/v1"
)
var client = &http.Client{Timeout: 10 * time.Second}
func api(method string, args ...interface{}) (string, error) {
if args == nil {
args = []interface{}{}
}
b, err := json.Marshal(args)
if err != nil {
return "", err
}
q := url.Values{
"access_key": {accessKey},
"secret_key": {secretKey},
"method": {method},
"args": {string(b)},
}
resp, err := client.Get(baseAPI + "?" + q.Encode())
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func main() {
ret, err := api("CommandRobot", 186515, "ok12345")
fmt.Println(ret, err)
}
参考:
扩展API接口详解
所有方法都通过https://www.fmz.com/api/v1调用,请求格式与签名见签名验证,返回结构与错误码见扩展API接口返回码。各方法页面示例中的api()即签名验证页面Python示例中的函数。
方法一览
| 对象 | 方法 | 参数(按顺序,方括号内可省略) | 说明 | 注意 |
|---|---|---|---|---|
| 账号 | GetAccount | 无 | 账号信息 | 只读 |
| 托管者 | GetNodeList | [offset, limit] | 托管者列表 | 只读 |
| 托管者 | DeleteNode | nid | 删除托管者 | 删除,不可恢复 |
| 交易所 | GetExchangeList | isSummary | 平台支持的交易所及配置项 | 只读 |
| 交易所 | GetPlatformList | [offset, limit] | 已添加的交易所账户 | 只读 |
| 策略 | GetStrategyList | offset, length, strategyType, category, language, kw[, groupId, orderBy] | 策略列表 | 只读 |
| 实盘 | GetRobotGroupList | 无 | 实盘分组 | 只读 |
| 实盘 | GetRobotList | [offset, length, customStatus, appId, kw, groupId, orderBy, strategyId] | 实盘列表 | 只读 |
| 实盘 | GetRobotDetail | robotId | 实盘详细信息 | 只读 |
| 实盘 | GetRobotLogs | robotId, logMinId, …, summaryLimit[, logExchange, logKeyword, logTypes] | 日志、收益、图表与状态栏数据 | 只读 |
| 实盘 | NewRobot | settings | 创建并启动实盘 | 扣费;实盘会真实交易 |
| 实盘 | RestartRobot | robotId[, settings] | 启动(重启)实盘 | 扣费;实盘会真实交易 |
| 实盘 | StopRobot | robotId | 停止实盘 | 不会平仓 |
| 实盘 | CommandRobot | robotId, cmd | 向实盘发送交互命令 | 策略可能据此下单 |
| 实盘 | DeleteRobot | robotId[, removeLog] | 删除实盘 | 删除,不可恢复 |
| 调试 | PluginRun | settings | 在托管者上执行一段代码 | 代码可以真实下单 |
API KEY需要有对应方法的权限,见创建ApiKey。
参数传法
args有两种写法:
- 数组:按上表的顺序传位置参数,例如
[123, "ok"]。 - 对象:按参数名传值,例如
{"robotId": 123, "cmd": "ok"}。参数名不区分大小写、忽略下划线,没有传的参数取默认值。可选参数多的方法(GetRobotList、GetRobotLogs、GetStrategyList)建议用这种写法。
实盘配置(settings)
NewRobot、RestartRobot、PluginRun的settings参数是一个JSON对象,常用字段如下:
| 字段 | 说明 |
|---|---|
| name | 实盘名称。 |
| strategy | 策略Id,可用GetStrategyList查询。RestartRobot不能更换实盘的策略。 |
| args | 策略参数,每个元素为["参数名", 值],例如[["Interval", 500]];策略没有参数时为[]。 |
| exchanges | 交易所对象配置数组,每个元素对应一个交易所对象,写法见下文。 |
| period | 默认K线周期,单位为秒,例如60、3600。 |
| node | 运行实盘的托管者Id,可用GetNodeList查询;不写或为-1时自动分配。 |
| group | 实盘分组Id,可用GetRobotGroupList查询。 |
| appid | 自定义标签,GetRobotList可以按标签筛选。 |
exchanges的元素有两种写法,同一个数组中不能混用(以第一个元素的写法为准):
- 引用平台上已添加的交易所账户:
{"pid": 123, "pair": "BTC_USDT"}。pid可用GetPlatformList查询(返回数据中的id)。 - 直接传入交易所配置:
{"eid": "Binance", "label": "test", "pair": "BTC_USDT", "meta": {"AccessKey": "...", "SecretKey": "..."}}。eid为交易所Id;meta的字段名见GetExchangeList返回的meta;label是交易所对象的标签,策略中用exchange.GetLabel()获取。平台不保存meta中的密钥,而是直接转发给托管者,所以用这种写法创建的实盘,每次重启时都必须重新传入settings。
通用协议交易所的写法:{"eid": "Exchange", "label": "test", "pair": "BTC_USDT", "meta": {"AccessKey": "...", "SecretKey": "...", "Front": "http://127.0.0.1:6666/test"}},Front为通用协议服务的地址。
GetAccount
GetAccount方法用于获取请求中的API KEY对应的发明者量化交易平台账号的账户信息。
返回值
json
{
"code":0,
"data":{
"result":{
"balance":22944702436,
"concurrent":0,
"consumed":211092719653,
"email":"[email protected]",
"openai":false,
"settings":null,
"sns":{"wechat":true},
"uid":"105ea6e51bcc177926a10fdbb7e2a1d6",
"username":"abc"
},
"error":null
}
}
- balance: 账户余额,单位为USD。为了控制精度使用整数表示,除以1e8(10的8次方)得到实际数值,示例中为229.44702436。
- consumed: 累计消费金额,单位与换算方式同
balance。
参数
无参数
GetNodeList
GetNodeList方法用于获取请求中的API KEY对应的平台账号可以使用的托管者列表,包括自己的托管者和平台的公共托管者。
返回值
json
{
"code": 0,
"data": {
"result": {
"all": 1,
"nodes": [{
"build": "3.7",
"city": "...",
"created": "2024-11-08 09:21:08",
"date": "2024-11-08 16:37:16",
"forward": "...",
"guid": "...",
"host": "node.fmz.com:9902",
"id": 123,
"ip": "...",
"is_owner": true,
"loaded": 0,
"name": "MacBook-Pro-2.local",
"online": true,
"os": "darwin/amd64",
"peer": "...",
"public": 0,
"region": "...",
"tunnel": false,
"version": "...",
"wd": 0
}]
},
"error": null
}
}
返回值字段说明(字面意思较明显的不再赘述):
- all: 托管者总数(包括公共托管者)。
- nodes: 记录托管者节点详细信息。
- build: 版本号。
- city: 所在城市。
- is_owner: true表示是私有托管者,false表示是公共托管者。
- loaded: 负载,搭载策略实例的个数。
- public: 0表示私有托管者,1表示公共托管者。
- region: 地理位置。
- version: 托管者详细版本信息。
- wd: 是否开启离线报警,0表示未开启。
一键部署的托管者包含一些额外信息,字段以ecs_、unit_前缀开头,记录了一键部署托管者服务器的相关信息(运营商名称、配置、状态等),计费周期、价格等信息,不再赘述。
参数
| 名称 | 类型 | 必填 | 描述 |
offset | number | 否 | 分页偏移,默认为0。 |
limit | number | 否 | 每页数量;不传或小于等于0时返回全部。 |
DeleteNode
DeleteNode方法用于删除请求中API KEY对应的发明者量化交易平台账号下的托管者节点,删除的托管者节点ID为nid参数指定的托管者ID。
返回值
json
{
"code":0,
"data":{
"result":true,
"error":null
}
}
- result: 是否成功删除关联的托管者程序。
参数
| 名称 | 类型 | 必填 | 描述 |
nid | number | 是 |
|
GetExchangeList
GetExchangeList方法用于获取FMZ量化交易平台支持的交易所列表及其配置信息。
返回值
isSummary参数为false时,返回的数据:
json
{
"code": 0,
"data": {
"result": {
"exchanges": [{
"category": "加密货币||Crypto",
"eid": "Futures_Binance",
"id": 74,
"logo": "/upload/asset/d8d84b23e573e9326b99.svg",
"meta": "[{\"desc\": \"Access Key\", \"qr\":\"apiKey\",\"required\": true, \"type\": \"string\", \"name\": \"AccessKey\", \"label\": \"Access Key\"}, {\"encrypt\": true, \"qr\":\"secretKey\",\"name\": \"SecretKey\", \"required\": true, \"label\": \"Secret Key\", \"type\": \"password\", \"desc\": \"Secret Key\"}]",
"name": "币安期货|Futures_Binance",
"priority": 200,
"stocks": "BTC_USDT,ETH_USDT,ETH_USD",
"website": "https://accounts.binance.com/zh-TC/register?ref=45110270"
}]
},
"error": null
}
}
isSummary参数为true时,返回的数据:
json
{
"code": 0,
"data": {
"result": {
"exchanges": [{
"category": "加密货币||Crypto",
"eid": "Futures_Binance",
"id": 74,
"logo": "/upload/asset/d8d84b23e573e9326b99.svg",
"name": "币安期货|Futures_Binance",
"priority": 200,
"website": "https://accounts.binance.com/zh-TC/register?ref=45110270"
}]
},
"error": null
}
}
- meta: 交易所配置元数据。
参数
| 名称 | 类型 | 必填 | 描述 |
isSummary | bool | 是 |
|
GetPlatformList
GetPlatformList方法用于获取请求中的API KEY对应的发明者量化交易平台账号下的已添加的交易所列表。
返回值
json
{
"code": 0,
"data": {
"result": {
"all": 2,
"platforms": [{
"category": "加密货币||Crypto",
"date": "2023-12-07 13:44:52",
"eid": "Binance",
"id": 123,
"label": "币安",
"logo": "...",
"name": "币安现货|Binance",
"stocks": ["BTC_USDT", "LTC_USDT", "ETH_USDT", "ETC_USDT", "BTC_TUSD", "ETH_TUSD", "BNB_TUSD"],
"website": "..."
}, {
"category": "通用协议|Custom Protocol",
"date": "2020-11-09 11:23:48",
"eid": "Exchange",
"id": 123,
"label": "XX交易所REST协议",
"logo": "...",
"name": "通用协议|Custom Protocol",
"stocks": ["BTC_USDT", "ETH_USDT"],
"website": ""
}]
},
"error": null
}
}
- all: 已添加/配置的交易所对象个数。
- platforms: 交易所相关信息。
- eid: 在发明者量化交易平台上交易所的Id,一些配置、参数中会使用到
eid。
- eid: 在发明者量化交易平台上交易所的Id,一些配置、参数中会使用到
参数
| 名称 | 类型 | 必填 | 描述 |
offset | number | 否 | 分页偏移,默认为0。 |
limit | number | 否 | 每页数量;不传或小于等于0时返回全部。 |
GetStrategyList
GetStrategyList方法用于获取平台策略信息。
返回值
json
{
"code": 0,
"data": {
"result": {
"all": 123,
"strategies": [{
"category": 9,
"date": "2024-11-10 20:40:04",
"description": "",
"forked": 0,
"hits": 0,
"id": 123,
"is_buy": false,
"is_owner": false,
"language": 0,
"last_modified": "2024-11-11 17:23:52",
"name": "HedgeGridStrategy",
"profile": {
"avatar": "...",
"nickname": "abc",
"uid": "4ed225440db1eda23fe05ed10184113e"
},
"public": 0,
"tags": "",
"uid": "4ed225440db1eda23fe05ed10184113e",
"username": "abc"
}]
},
"error": null
}
}
- all: 筛选查询出的策略总数。
- strategies: 查询出的具体策略信息,其中
category、language的取值同上面的参数说明。
参数
| 名称 | 类型 | 必填 | 描述 |
offset | number | 是 | 分页偏移。 |
length | number | 是 | 每页数量;小于等于0时返回全部。 |
strategyType | number | 是 | 查询范围:
|
category | number | 是 | 策略类型:
|
language | number | 是 | 策略的编程语言:
|
kw | string | 是 | 按策略名称模糊匹配的关键字,多个词用空格分隔;空字符串表示不筛选。以 |
groupId | number | 否 | 策略分组: |
orderBy | string | 否 | 排序字段: |
备注
参数中没有needArgs。按旧版文档在category之后多传一个参数,会使后面的参数错位,请按上面的顺序传参,或按参数名传值:
plaintext
api('GetStrategyList', 0, 10, -3, -1, -1, '') # 自己的前10个策略
api('GetStrategyList', strategyType=-3, language=7) # 自己的全部Rust策略
GetRobotGroupList
GetRobotGroupList方法用于获取请求中的API KEY对应的发明者量化交易平台账号下的实盘分组列表。
返回值
json
{
"code": 0,
"data": {
"result": {
"items": [{
"id": 3417,
"name": "测试"
}, {
"id": 3608,
"name": "实盘演示"
}]
},
"error": null
}
}
- items: 实盘分组信息。
- id: 实盘分组Id。
- name: 实盘分组名称。
items字段只记录创建的新分组,「默认」分组不在items中。
参数
无参数
GetRobotList
GetRobotList方法用于获取请求中的API KEY对应的平台账号下的实盘列表。参数都可以省略。
返回值
json
{
"code": 0,
"data": {
"result": {
"all": 1,
"concurrent": 0,
"robots": [{
"charge_time": 1731654846,
"date": "2024-11-12 14:05:29",
"end_time": "2024-11-15 14:56:32",
"fixed_id": 4509153,
"id": 591026,
"is_sandbox": 0,
"name": "测试",
"node_guid": "45891bcf3d57f99b08a43dff76ee1ea1",
"node_id": 4519153,
"node_public": 0,
"profit": 0,
"public": 0,
"refresh": 1731651257000,
"start_time": "2024-11-15 14:56:30",
"status": 3,
"strategy_id": 411670,
"strategy_isowner": true,
"strategy_language": 0,
"strategy_name": "测试",
"strategy_public": 0,
"uid": "105ed6e511cc977921610fdbb7e2a1d6",
"wd": 0
}]
},
"error": null
}
}
- all: 符合条件的实盘总数。
- robots: 实盘信息,
status为实盘状态码。- group_id: 实盘分组Id;如果策略实盘在默认分组中则没有
group_id字段。
- group_id: 实盘分组Id;如果策略实盘在默认分组中则没有
参数
| 名称 | 类型 | 必填 | 描述 |
offset | number | 否 | 分页偏移,默认为0。 |
length | number | 否 | 每页数量;小于等于0时返回全部(默认)。 |
customStatus | number | 否 | 按实盘状态码筛选,见 |
appId | string | 否 | 按实盘的自定义标签(创建时 |
kw | string | 否 | 按实盘名称模糊匹配的关键字,空字符串表示不筛选。 |
groupId | number | 否 | 实盘分组: |
orderBy | string | 否 | 排序字段: |
strategyId | number | 否 | 大于0时只返回该策略的实盘,默认为0(不筛选)。 |
备注
以签名验证页面Python示例中的api()为例:
api('GetRobotList'):获取全部实盘。api('GetRobotList', 'member2'):只传一个字符串时视为标签,获取标签为member2的全部实盘。api('GetRobotList', 0, 100, -1, 'member2', ''):按位置传参,从第0条开始最多获取100个标签为member2的实盘。api('GetRobotList', appId='member2', length=100):按参数名传值,效果同上。
GetRobotDetail
GetRobotDetail方法用于获取请求中的API KEY对应的发明者量化交易平台账号下的实盘详细信息,所要被获取详细信息的实盘Id为robotId参数指定的实盘Id。
返回值
json
{
"code": 0,
"data": {
"result": {
"robot": {
"charge_time": 1732246539,
"charged": 5850000,
"consumed": 5375000000,
"date": "2018-12-28 14:34:51",
"favorite": {
"added": false,
"type": "R"
},
"fixed_id": 123,
"hits": 1,
"id": 123,
"is_deleted": 0,
"is_manager": true,
"is_sandbox": 0,
"name": "测试",
"node_id": 123,
"pexchanges": {
"123": "Futures_OKX"
},
"phash": {
"123": "ca1aca74b9cf7d8624f2af2dac01e36d"
},
"plabels": {
"123": "OKX期货"
},
"priority": 0,
"profit": 0,
"public": 0,
"refresh": 1732244453000,
"robot_args": "[]",
"start_time": "2024-11-22 11:00:48",
"status": 1,
"strategy_args": "[]",
"strategy_exchange_pairs": "[60,[123],[\"ETH_USDT\"]]",
"strategy_id": 123,
"strategy_last_modified": "2024-11-21 16:49:25",
"strategy_name": "测试",
"strategy_public": "0",
"uid": "105ed6e51bcc17792a610fdbb7e2a1d6",
"username": "abc",
"wd": 0
}
},
"error": null
}
}
- charge_time: 下次扣费时间(Unix时间戳,秒),即当前已付费时段的截止时间。
- charged: 累计计费时长,单位为秒。
- consumed: 累计扣费金额,单位为USD,按1e8放大为整数,示例中5375000000即53.75 USD。
- date: 创建日期。
- fixed_id: 实盘运行时指派的托管者ID,如果是自动,该值为-1。
- is_manager: 是否有权限管理该实盘。
- is_sandbox: 是否是模拟盘。
- name: 实盘名称。
- node_id: 托管者ID。
- pexchanges: 实盘配置的交易所对象,123为pid,"Futures_OKX"为交易所Id(eid)。
- plabels: 实盘配置的交易所对象的标签信息。
- profit: 实盘收益数据。
- public: 实盘是否公开。
- refresh: 最近活跃时间。
- strategy_exchange_pairs: 配置的交易所对象,设置的交易对信息。
- wd: 是否开启离线报警。
参数
| 名称 | 类型 | 必填 | 描述 |
robotId | number | 是 |
|
备注
strategy_exchange_pairs属性说明,用以下数据为例:
plaintext
"[60,[44314,42960,15445,14703],[\"BTC_USDT\",\"BTC_USDT\",\"ETH_USDT\",\"ETH_USDT\"]]"
其中第一个数据60,代表实盘设置的默认K线周期为1分钟,即60秒。
[44314,42960,15445,14703]为实盘配置的交易所对象的pid(按添加顺序)。
[\"BTC_USDT\",\"BTC_USDT\",\"ETH_USDT\",\"ETH_USDT\"]为实盘配置的交易所对象设置的交易对(按添加顺序与pid一一对应)。
GetRobotLogs
GetRobotLogs方法用于获取请求中的API KEY对应的发明者量化交易平台账号下的实盘日志信息,所要被获取日志信息的实盘Id为robotId参数指定的实盘Id。
返回值
json
{
"code": 0,
"data": {
"result": {
"chart": "",
"chartTime": 0,
"logs": [{
"Total": 20,
"Max": 20,
"Min": 1,
"Arr": []
}, {
"Total": 0,
"Max": 0,
"Min": 0,
"Arr": []
}, {
"Total": 0,
"Max": 0,
"Min": 0,
"Arr": []
}],
"node_id": 123,
"online": true,
"refresh": 1732201544000,
"status": 4,
"summary": "...",
"updateTime": 1732201532636,
"wd": 0
},
"error": null
}
}
- logs: 日志信息;查询出的若干条日志数据在Arr字段中。
logs中第一个数据结构为实盘数据库中策略日志表中的日志记录。
logs中第二个数据结构为实盘数据库中收益日志表中的日志记录。
logs中第三个数据结构为实盘数据库中图表日志表中的日志记录。 - summary: 实盘状态栏数据。
参数
| 名称 | 类型 | 必填 | 描述 |
robotId | number | 是 |
|
logMinId | number | 是 |
|
logMaxId | number | 是 |
|
logOffset | number | 是 |
|
logLimit | number | 是 |
|
profitMinId | number | 是 |
|
profitMaxId | number | 是 |
|
profitOffset | number | 是 |
|
profitLimit | number | 是 |
|
chartMinId | number | 是 |
|
chartMaxId | number | 是 |
|
chartOffset | number | 是 |
|
chartLimit | number | 是 |
|
chartUpdateBaseId | number | 是 |
|
chartUpdateDate | number | 是 |
|
summaryLimit | number | 是 |
|
logExchange | string | 否 | 只返回该交易所对象(按标签)的日志,空字符串表示不筛选。 |
logKeyword | string | 否 | 只返回日志内容包含该关键字的日志,空字符串表示不筛选。 |
logTypes | string | 否 | 只返回这些类型的日志,日志类型号用逗号分隔,例如 |
备注
-
数据库中的策略日志表
返回数据中logs的属性值(数组结构)的第一个元素中(日志数据)Arr属性值描述如下:plaintext"Arr": [ [3977, 3, "Futures_OKX", "", 0, 0, "Sell(688.9, 2): 20016", 1526954372591, "", ""], [3976, 5, "", "", 0, 0, "this_week 仓位过多, 多: 2", 1526954372410, "", ""] ],id logType eid orderId price amount extra date contractType direction 3977 3 "Futures_OKX" "" 0 0 "Sell(688.9, 2): 20016" 1526954372591 "" "" 3976 5 "" "" 0 0 "this_week 仓位过多, 多: 2" 1526954372410 "" "" extra为打印的日志的附加消息。logType值具体代表的日志类型描述如下:logType: 0 1 2 3 4 5 6 logType意义: BUY SALE RETRACT ERROR PROFIT MESSAGE RESTART 中文意义 买单类型日志 卖单类型日志 撤销 错误 收益 日志 重启 -
数据库中的收益图表日志表
该图表日志表数据与策略日志表中的收益日志一致。plaintext"Arr": [ [202, 2515.44, 1575896700315], [201, 1415.44, 1575896341568] ]以其中一条日志数据为例:
plaintext[202, 2515.44, 1575896700315]202为日志Id,2515.44为收益数值,1575896700315为时间戳。 -
数据库中的图表日志表
plaintext"Arr": [ [23637, 0, "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"], [23636, 5, "{\"x\":1575960300000,\"y\":3.0735}"] ]以其中一条日志数据为例:
plaintext[23637, 0, "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"],23637为日志Id,0为图表数据系列索引,最后的数据"{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"为日志数据,这条数据为图表上的K线数据。
NewRobot
NewRobot方法用于在请求中的API KEY对应的平台账号下创建一个实盘并启动运行,与在网页上创建实盘一样会扣费。
返回值
json
{
"code":0,
"data":{
"result":591988,
"error":null
}
}
- result: 创建成功时为新实盘的Id;失败时为负数,含义同
实盘状态码中的异常代码(例如-2没有找到托管者,-5余额不足)。
参数
| 名称 | 类型 | 必填 | 描述 |
settings | JSON对象 | 是 | 实盘配置,字段见
|
备注
用eid方式直接传入交易所配置时,平台不保存meta中的密钥,之后每次用RestartRobot重启这个实盘都必须传入settings。
RestartRobot
RestartRobot方法用于启动(重启)请求中的API KEY对应的平台账号下的实盘,实盘Id由robotId参数指定。启动会扣费。
返回值
json
{
"code":0,
"data":{
"result":1,
"error":null
}
}
- result: 实盘状态码,1即运行中。
参数
| 名称 | 类型 | 必填 | 描述 |
robotId | number | 是 | 实盘Id,可以用 |
settings | JSON对象 | 否 | 实盘配置,字段见 |
备注
在平台页面创建、使用已添加交易所账户(pid)的实盘,可以只传robotId,按实盘当前的配置启动。用eid方式直接传入交易所配置的实盘(通常由扩展API接口创建),平台没有保存密钥,每次重启都必须传入settings。
StopRobot
StopRobot方法用于停止请求中API KEY对应的发明者量化交易平台账号下的实盘。停止运行的实盘Id由robotId参数指定。
返回值
json
{
"code":0,
"data":{
"result":2,
"error":null
}
}
- result: 实盘状态码,2表示停止中。
参数
| 名称 | 类型 | 必填 | 描述 |
robotId | number | 是 |
|
CommandRobot
CommandRobot方法用于向请求中的API KEY对应的发明者量化交易平台账号下的实盘发送交互命令,接收交互命令的实盘Id为robotId参数指定的实盘Id,交互命令由策略中调用的GetCommand()函数捕获返回。
返回值
json
{
"code":0,
"data":{
"result":true,
"error":null
}
}
- result: 交互指令是否发送成功;向一个没有运行的实盘发送指令,返回的数据中result为false。
参数
| 名称 | 类型 | 必填 | 描述 |
robotId | number | 是 |
|
cmd | string | 是 | 发送给实盘的交互命令,策略中用 |
备注
实盘策略,假设这个策略实盘处于运行中,实盘Id为123:
javascript
function main() {
while (true) {
var cmd = GetCommand()
if (cmd) {
Log(cmd)
}
Sleep(2000)
}
}
用签名验证页面Python示例中的api()调用api("CommandRobot", 123, "test command"),Id为123的实盘会收到交互指令:test command,然后通过Log函数输出打印出来。
DeleteRobot
DeleteRobot方法用于删除请求中的API KEY对应的平台账号下的实盘,实盘Id由robotId参数指定。运行中的实盘需要先停止才能删除。删除后不可恢复。
返回值
json
{
"code":0,
"data":{
"result":0,
"error":null
}
}
- result: 删除操作的结果。
- 0:删除成功。
- -1:没有删除:实盘不存在,或者仍在运行、启动中、停止中。
- -2:实盘已删除,但无法与实盘所在的托管者联系,日志数据没有删除;需要到托管者目录
logs/storage/<实盘Id>/下手动删除(例如123.db3)。
参数
| 名称 | 类型 | 必填 | 描述 |
robotId | number | 是 | 要删除的实盘Id,可以用 |
removeLog | bool | 否 | 是否同时删除托管者上的实盘日志数据,默认为 |
PluginRun
PluginRun方法在托管者上执行一段JavaScript代码并返回结果,与开发工具中的「调试工具」、交易终端插件使用同一种执行机制(见插件原理与编写)。执行时不创建实盘、不计费,单次执行最长5分钟。
返回值
json
{
"code": 0,
"data": {
"result": "{\"logs\":[{\"PlatformId\":\"\",\"OrderId\":\"0\",\"LogType\":5,\"Price\":0,\"Amount\":0,\"Extra\":\"Hello FMZ\",\"Currency\":\"\",\"Instrument\":\"\",\"Direction\":\"\",\"Time\":1732267473108}],\"result\":\"\"}",
"error": null
}
}
- result: 执行结果,是一个JSON字符串:
logs为代码中Log()输出的日志,result为main()返回值的JSON文本。
参数
| 名称 | 类型 | 必填 | 描述 |
settings | JSON对象 | 是 | 执行配置,例如:
|
备注
exchanges也可以不引用平台上的交易所账户,直接传入交易所配置,例如:
plaintext
{"eid": "Binance", "pair": "ETH_BTC", "meta": {"AccessKey": "...", "SecretKey": "..."}}
meta的字段名见GetExchangeList返回的meta。exchanges中通常只设置一个交易所对象(调试工具页面也只支持一个);设置两个不会报错,但代码中访问第二个交易所对象时会报错。
扩展API接口返回码
扩展API接口返回的结构如下:
json
{
"code": 0,
"data": {
"result": null,
"error": null
}
}
code是请求本身的状态码:
| 描述 | 代码 |
|---|---|
| 执行成功 | 0 |
错误的API KEY:AccessKey不存在或已禁用;直接验证时secret_key不正确 | 1 |
| 错误的签名 | 2 |
Nonce错误:nonce不大于上次请求的值,或与服务器时间相差超过1小时 | 3 |
| 方法不正确:方法不存在、不对外开放,或这把API KEY没有该方法的权限 | 4 |
参数不正确:args不是合法的JSON,或调用失败 | 5 |
| 内部未知错误 | 6 |
| 请求来源IP不在这把API KEY的IP白名单内 | 7 |
code为0只表示请求被受理。方法的结果在data.result中;方法执行出错时,data.error为错误信息(成功时为null)。例如参数个数不对:
json
{
"code": 0,
"data": {
"result": null,
"error": "Params number mismatch for StopRobot: expected 1, got 0"
}
}
交易终端
平台提供模块化、可定制的交易终端页面:可以自由添加行情、交易等各种模块,模块可以拖动、缩放,可以修改模块绑定的交易所、交易对,同一类模块可以添加多个,方便手动交易和半程序化交易。
交易终端还支持交易插件:自己编写一段代码作为模块,在选定的托管者上执行,辅助手动交易。
插件原理与编写
原理
交易插件是一段在托管者上执行的短代码:在交易终端页面点击「执行」时,平台把插件代码和模块选定的交易所账户发送到选定的托管者执行,执行结束后把返回值显示在模块中。下面几个入口使用同一种执行机制:
| 入口 | 执行的代码 | 说明 |
|---|---|---|
| 交易终端插件 | 策略库中类型为「交易插件」的策略 | 在交易终端页面添加、执行 |
| 调试工具(开发工具) | 页面中临时编写的JavaScript代码 | 用于测试API调用 |
扩展API接口PluginRun | 请求中的source,或账号中已有的策略 | 供程序调用 |
MCP的plugin_*工具 | 平台内置的查询行情、下单等函数 | 供AI助手调用,按trade权限授权,见AI接入 |
这种执行方式不创建实盘、不计费,单次执行最长5分钟,超时中断。适合辅助手动交易的简单任务,例如冰山委托、批量挂单撤单、计算;需要长期运行的逻辑应创建实盘。
编写
在新建策略页面把策略类型设置为「交易插件」即可创建交易插件。交易插件、调试工具和PluginRun都只支持JavaScript。
插件的入口是main(),返回值就是执行结果:返回表格对象、图表对象时,在模块中显示为表格、图表(示例见插件示例)。插件中Log()输出的日志不会在模块中显示。
使用
- 添加:在交易终端页面打开模块添加菜单,账号策略库中的交易插件会出现在列表中,选择需要的插件添加。
- 执行:点击插件模块中的「执行」运行插件。
数据目录
插件和调试工具在托管者上执行时,以托管者运行目录下的logs/storage/p<数字>/为工作目录(每个平台账号一个以p开头的目录,第一次执行后创建)。如果交易终端使用的交易所账户以密钥文件路径(file:///xxx.txt)的方式配置密钥,需要把密钥文件放在这个目录中。
插件示例
插件可以在一段时间内执行代码,完成一些简单的操作,例如冰山委托、挂单、撤单、计算等。插件用return返回结果,返回表格、图表对象时直接显示为表格、图表。下面是两个例子,更多范例可以在策略广场中查找,例如逐笔小量买入/卖出。
返回深度快照
以表格显示当前盘口前15档:
javascript
// 返回深度快照
function main() {
var tbl = {
type: 'table',
title: '深度快照 @ ' + _D(),
cols: ['#', 'Amount', 'Ask', 'Bid', 'Amount'],
rows: []
}
var d = exchange.GetDepth()
var n = Math.min(d.Asks.length, d.Bids.length, 15)
for (var i = 0; i < n; i++) {
tbl.rows.push([i, d.Asks[i].Amount, d.Asks[i].Price + '#ff0000', d.Bids[i].Price + '#0000ff', d.Bids[i].Amount])
}
return tbl
}
画跨期差价
期货交易所对象上,取季度合约与当周合约的5分钟K线,画出收盘价差:
javascript
// 画跨期差价
var chart = {
__isStock: true,
title: {text: '差价分析图'},
xAxis: {type: 'datetime'},
yAxis: {
title: {text: '差价'},
opposite: false
},
series: [
{name: "diff", data: []}
]
}
function main() {
exchange.SetContractType('quarter')
var recordsA = exchange.GetRecords(PERIOD_M5)
exchange.SetContractType('this_week')
var recordsB = exchange.GetRecords(PERIOD_M5)
var n = Math.min(recordsA.length, recordsB.length)
for (var i = 0; i < n; i++) {
var a = recordsA[recordsA.length - n + i]
var b = recordsB[recordsB.length - n + i]
chart.series[0].data.push([a.Time, a.Close - b.Close])
}
return chart
}