Type/to search
Getting Started
Welcome to FMZ Quant Trading Platform
Quick Start
Key Security
Platform Basics
Account and Billing
Live Robot Billing and Top-up
Sub-accounts
Exchange
General Protocol
Local Credential Files
Exchange-Specific Notes
Securities and Futures
Crypto
Docker
Strategy Library
Live Trading
Writing Strategies
Development Tools
Backtesting System
Advanced Topics
Data and Research
Integrations

Driving the platform from AI assistants or other programs: AI integration (the MCP service), the extended API and trading terminal plugins.

Connect FMZ to an AI coding assistant (AI agent) such as Claude Code, Codex or Cursor, and you can write strategies, run backtests, create and manage live trading, and read logs and profit just by talking to it. The assistant reaches these functions through the platform's MCP (Model Context Protocol) service, with an API KEY created for it alone; you choose its permissions when you approve it and can change or revoke them at any time.

Connect with one sentence

Tell your AI assistant:

Read https://www.fmz.com/agent/setup.md and connect to FMZ

The assistant follows that page; your only step is one click in the browser:

  1. The assistant requests authorization and shows you a link such as https://www.fmz.com/agent/authorize?code=XXXX-XXXX, valid for 10 minutes.
  2. Open it in a browser where you are logged in to FMZ, check the requester's name and the permissions, and approve. You can untick permissions you do not want to grant.
  3. The assistant receives an API KEY, writes it into its own MCP configuration and connects. From then on you can simply ask it to "list my live trading" or "backtest this strategy".

The assistant names the key "name @ machine" (for example Claude Code @ MacBook). When a request with the same name is approved again, the old key is revoked and replaced, so connecting again does not pile up keys.

Permissions

PermissionAllowsDefault
readLists and details of strategies, live trading, nodes and exchange accounts; logs, messages, account summary (never any secret)Yes
backtestStart, query and stop backtestsYes
writeSave strategies and versions, groups, alert switches; change the configuration of stopped live tradingYes
tradeCreate, start and stop live trading, send interactive commands (costs balance and places real orders)Yes
dangerDelete strategies, live trading and nodes; publish strategiesNo

danger is never granted by default: the assistant has to request it explicitly and you have to tick it on the approval page. Before calling a [trade] or [danger] tool the assistant should ask you first.

Manual configuration

Clients that cannot run commands (for example Cherry Studio) can be configured by hand:

  1. Create an API KEY under Account settings → API KEY (https://www.fmz.com/m/account#apikey) and note its Access Key and Secret Key.
  2. Add an MCP server of type Streamable HTTP to the client:
    • URL: https://www.fmz.com/api/mcp/<Access Key>
    • Header: Authorization: Bearer <Secret Key>

The Secret Key goes in the header only, never in the URL. Examples:

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>"}}}}

The JSON is for Cursor (~/.cursor/mcp.json) and other clients that speak Streamable HTTP; Claude Desktop needs the npx mcp-remote bridge described in the setup page.

Install the skills (recommended)

The skills are knowledge packs written for AI assistants: platform workflow, the full API reference, how to write strategies in each language, backtesting and indicators. With them installed the assistant writes noticeably more accurate strategies. In a terminal:

bash
npx skills add fmzquant/skills --global --yes -a claude-code

Put your assistant's name after -a (claude-code, codex, cursor, gemini-cli, ...). They can also be read on GitHub: https://github.com/fmzquant/skills.

Available tools

Once connected the assistant can use the tools below; the list the assistant sees is authoritative:

PermissionTools
readping, 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
backtestrun_backtest, get_backtest, list_backtests, stop_backtest
writecheck_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
tradecreate_robot, start_robot, stop_robot, restart_robot, send_robot_command, and the trading terminal plugin tools plugin_* (market data, orders, ...)
dangerdelete_strategy, delete_robot, delete_node, publish_strategy

A typical session: list_platforms and list_nodes to see which exchange accounts and nodes the account has; save_strategy to save a strategy and check_strategy to check it; run_backtest and get_backtest to backtest; then create_robot to go live and get_robot / get_robot_logs to watch it.

Security and management

  • Exchange API KEYs never pass through the assistant: add them on the Exchanges page of the website and the assistant picks them by id. No tool result ever contains a secret.
  • At https://www.fmz.com/m/account#apikey you can see the key the assistant uses, change its permissions or lock it. Besides the permission names above, permissions can list tool names, and !tool_name excludes one tool.
  • When you are done, ask the assistant to call revoke_my_key to revoke its own key, or delete it on that page.
  • stop_robot stops live trading; it does not close positions.
  • The first time the assistant creates live trading, backtest first and then run on a demo account or with a small amount.

Common problems

  • No online node: live trading needs at least one online node, see Platform Basics → Nodes.
  • Too many backtests: concurrent backtests are limited; have the assistant stop_backtest the ones it no longer needs.
  • The assistant says a tool is missing or a call is refused: the key lacks that permission; change it on the API KEY page and reconnect.

The same API KEY also works with the Extended API (Integrations → Extended API Interface) for scripts and schedulers; prefer MCP wherever it can be used.

The extended API is the platform's HTTP interface (https://www.fmz.com/api/v1) for scripts, scheduled jobs and other programs: query the account, dockers, strategies and live trading bots, create, restart and stop bots, send interactive commands to bots, and so on.

To operate the platform interactively from an AI assistant (Claude Code, Cursor, ...), prefer AI Integration (the MCP service, see Integrations → AI Integration): it has more tools, takes named parameters, and can be authorized by permission category. Both can use the same API KEY.

Steps: create an API KEY (Create ApiKey), send requests as described in Authentication Methods, and look up methods and parameters in Extended API Interface Details.

On the Account Settings → API KEY page (/m/account#apikey), click "Create New ApiKey" to get an AccessKey and a SecretKey. The SecretKey carries every permission of the key; keep it secret. The same page lets you change the permissions of existing keys, or disable and delete them.

Permissions

When creating or editing a key, enter a comma-separated list in the "API Permissions" field:

  • *: allow all extended API methods.
  • Method names: allow only the listed methods, e.g. GetRobotList,GetRobotDetail,CommandRobot.
  • !MethodName: exclude a method, usually together with *, e.g. *,!DeleteRobot,!DeleteNode.

The same API KEY can also be used for AI Integration (the MCP service, see Integrations → AI Integration). Besides tool names, MCP tools can be authorized by permission category: read, backtest, write, trade, danger (see the AI Integration page), and !name excludes as well. Categories only apply to MCP tools; the extended API only recognizes method names and *.

With an empty permission list the extended API does not restrict methods (MCP allows every tool except danger). Grant only what each use needs, e.g. a key used only for TradingView alerts should get CommandRobot and nothing else.

The extended API supports two authentication methods:

  • Signature authentication: the request parameters are signed with the SecretKey, which itself never travels over the network. Programs should use this method.
  • Direct verification: the SecretKey is put into the request URL itself; meant for webhooks such as TradingView that accept only a single URL.

Request format

Send a POST request to https://www.fmz.com/api/v1 with the parameters as a form (application/x-www-form-urlencoded). The server also accepts the same parameters in the URL query string of a GET request, but then they end up in access logs along the way, so POST is recommended.

ParameterDescription
versionVersion, always 1.0.
access_keyThe AccessKey of the API KEY.
methodMethod name, e.g. GetNodeList.
argsMethod parameters as a JSON string: an array in parameter order (e.g. [], [123, "ok"]), or an object keyed by parameter name (e.g. {"robotId": 123}), see Extended API Interface Details. Treated as [] when omitted.
nonceTimestamp in milliseconds. It must be within 1 hour of server time and greater than the nonce of this API KEY's previous request.
signSignature, computed as described below.

The request does not contain the SecretKey.

Signature

Concatenate the string below, where args is the exact JSON string being submitted:

plaintext
version + "|" + method + "|" + args + "|" + nonce + "|" + secretKey

Compute the MD5 of the result and use its 32-character lowercase hexadecimal form as sign.

Python example

python
import hashlib import json import time import urllib.parse import urllib.request ACCESS_KEY = '' # AccessKey of the API KEY SECRET_KEY = '' # SecretKey of the API KEY def api(method, *args, **kwargs): d = { 'version': '1.0', 'access_key': ACCESS_KEY, 'method': method, # Positional arguments are sent as an array, keyword arguments as an object (by name) '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')) # Docker list print(api('GetRobotList', appId='member2')) # By name: bots labeled member2 print(api('CommandRobot', 123, 'ok')) # Send an interactive command to bot 123 print(api('GetRobotDetail', 123)) # Details of bot 123

Go example

mylang
package main import ( "crypto/md5" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "net/url" "strconv" "time" ) const ( accessKey = "" // AccessKey of the API KEY secretKey = "" // SecretKey of the API KEY 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) // Restart bot 123 with a new configuration; settings fields: see the bot configuration section in Extended API Interface Details 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) }

Direct verification computes no signature; the secret_key is put into the request parameters instead. This produces a fixed URL that can be entered into webhooks such as TradingView that accept only a single URL.

Security note: a secret_key in a URL ends up in browser history, proxy and server access logs, and the webhook provider's configuration; anyone who obtains the URL can call the API with this key's permissions. Use direct verification only for CommandRobot webhooks, and create a dedicated API KEY for it that is granted CommandRobot only (see Create ApiKey). If it leaks, delete that API KEY immediately.

The request parameters are access_key, secret_key, method and args (a JSON array, URL-encoded); version, nonce and sign are not needed. CommandRobot skips the nonce check; other methods are still checked: without a nonce the server uses the current time (to the second), so a second call within the same second returns a nonce error (code 3).

For example, with an API KEY whose AccessKey is xxx and SecretKey is yyy, opening the URL below sends the interactive command ok12345 to the live trading bot with ID 186515:

plaintext
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyy&method=CommandRobot&args=%5B186515%2C%22ok12345%22%5D

Receiving a webhook body

When the command argument of CommandRobot is an empty string and the request is a POST, the server sends the request body to the bot as the interactive command. For example, set the TradingView webhook URL to:

plaintext
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyy&method=CommandRobot&args=%5B186515%2C+%22%22%5D

The args value %5B186515%2C+%22%22%5D decodes to [186515, ""] (+ is a URL-encoded space): 186515 is the bot ID and the command is an empty string.

Simulating a TradingView webhook alert:

javascript
function main() { var options = { method: "POST", body: `{"test": 123}`, headers: {"Content-Type": "application/json"} } // A webhook alert sends a POST request with the required headers automatically return HttpQuery("https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyy&method=CommandRobot&args=%5B186515%2C+%22%22%5D", options) }

The content of the TradingView alert message box is the request body:

  • JSON format:

    plaintext
    {"close": {{close}}, "name": "aaa"}

    The bot with ID 186515 receives the interactive command {"close": 39773.75, "name": "aaa"}.

  • Text format:

    plaintext
    BTCUSDTPERP Crossing 39700.00 close: {{close}}

    The bot with ID 186515 receives the interactive command BTCUSDTPERP Crossing 39700.00 close: 39739.4.

Python and Go examples

python
import json import urllib.parse import urllib.request ACCESS_KEY = '' # AccessKey of an API KEY granted CommandRobot only 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')) # Without permission for the method the result is {'code': 4, 'data': None} print(api('CommandRobot', 186515, 'ok12345'))
mylang
package main import ( "encoding/json" "fmt" "io" "net/http" "net/url" "time" ) const ( accessKey = "" // AccessKey of an API KEY granted CommandRobot only 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) }

References:

All methods are called through https://www.fmz.com/api/v1; for the request format and signature see Authentication Methods → Signature Authentication, for the response structure and error codes see Extended API Interface Return Codes. The api() used in the examples on the method pages is the function from the Python example on the signature authentication page.

Method overview

ObjectMethodParameters (in order; bracketed ones may be omitted)DescriptionNotes
AccountGetAccountnoneAccount informationRead-only
DockerGetNodeList[offset, limit]Docker listRead-only
DockerDeleteNodenidDelete a dockerDeletes, cannot be undone
ExchangeGetExchangeListisSummaryExchanges supported by the platform and their settingsRead-only
ExchangeGetPlatformList[offset, limit]Exchange accounts you addedRead-only
StrategyGetStrategyListoffset, length, strategyType, category, language, kw[, groupId, orderBy]Strategy listRead-only
Live tradingGetRobotGroupListnoneLive trading groupsRead-only
Live tradingGetRobotList[offset, length, customStatus, appId, kw, groupId, orderBy, strategyId]Live trading listRead-only
Live tradingGetRobotDetailrobotIdLive trading detailsRead-only
Live tradingGetRobotLogsrobotId, logMinId, …, summaryLimit[, logExchange, logKeyword, logTypes]Logs, profit, chart and status bar dataRead-only
Live tradingNewRobotsettingsCreate and start a live trading botCharges fees; the bot trades for real
Live tradingRestartRobotrobotId[, settings]Start (restart) a live trading botCharges fees; the bot trades for real
Live tradingStopRobotrobotIdStop a live trading botDoes not close positions
Live tradingCommandRobotrobotId, cmdSend an interactive commandThe strategy may place orders on it
Live tradingDeleteRobotrobotId[, removeLog]Delete a live trading botDeletes, cannot be undone
DebuggingPluginRunsettingsRun a piece of code on a dockerThe code can place real orders

The API KEY needs permission for the method, see Create ApiKey.

Passing parameters

args can be written in two ways:

  • Array: positional parameters in the order of the table above, e.g. [123, "ok"].
  • Object: values by parameter name, e.g. {"robotId": 123, "cmd": "ok"}. Names are case-insensitive and underscores are ignored; omitted parameters take their defaults. Recommended for methods with many optional parameters (GetRobotList, GetRobotLogs, GetStrategyList).

Live trading configuration (settings)

The settings parameter of NewRobot, RestartRobot and PluginRun is a JSON object; common fields:

FieldDescription
nameName of the live trading bot.
strategyStrategy ID, see GetStrategyList. RestartRobot cannot change a bot's strategy.
argsStrategy parameters, each element ["name", value], e.g. [["Interval", 500]]; [] if the strategy has none.
exchangesArray of exchange object configurations, one element per exchange object, see below.
periodDefault K-line period in seconds, e.g. 60, 3600.
nodeID of the docker that runs the bot, see GetNodeList; omitted or -1 means automatic assignment.
groupLive trading group ID, see GetRobotGroupList.
appidCustom label; GetRobotList can filter by it.

An exchanges element takes one of two forms, which cannot be mixed in one array (the first element decides):

  • Reference an exchange account added on the platform: {"pid": 123, "pair": "BTC_USDT"}. pid is the id returned by GetPlatformList.
  • Pass the exchange configuration directly: {"eid": "Binance", "label": "test", "pair": "BTC_USDT", "meta": {"AccessKey": "...", "SecretKey": "..."}}. eid is the exchange ID; the field names of meta are given by the meta returned by GetExchangeList; label is the exchange object's label, read in the strategy with exchange.GetLabel(). The platform does not store the keys in meta but forwards them to the docker, so a bot created this way needs settings again on every restart.

For a custom-protocol exchange: {"eid": "Exchange", "label": "test", "pair": "BTC_USDT", "meta": {"AccessKey": "...", "SecretKey": "...", "Front": "http://127.0.0.1:6666/test"}}, where Front is the address of the custom-protocol service.

The GetAccount method is used to retrieve account information for the FMZ Quant Trading Platform account corresponding to the API KEY in the request.

Returns

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: Account balance in USD, stored as an integer for precision; divide by 1e8 (10 to the power of 8) to get the actual value, 229.44702436 in this example.
  • consumed: Total amount spent, same unit and conversion as balance.

Arguments

No parameters

The GetNodeList method returns the dockers available to the platform account of the API KEY in the request, including your own dockers and the platform's public dockers.

Returns

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 } }

Return value field descriptions (fields with obvious literal meanings are not elaborated):

  • all: Total number of dockers (public dockers included).
  • nodes: List of detailed information for docker nodes.
    • build: Version number.
    • city: City location.
    • is_owner: true indicates private docker, false indicates public docker.
    • loaded: Load amount, i.e., the number of currently running strategy instances.
    • public: 0 indicates private docker, 1 indicates public docker.
    • region: Geographic location.
    • version: Detailed version information of the docker.
    • wd: Offline alarm switch, 0 indicates not enabled.

One-click deployed dockers contain additional information, with related fields prefixed by ecs_ and unit_, recording information about the one-click deployed docker server (operator name, configuration, status, etc.), billing cycle, price, and other information, which will not be detailed here.

Arguments

NameTypeRequiredDescription

offset

number

No

Paging offset, default 0.

limit

number

No

Page size; omitted or less than or equal to 0 returns everything.

The DeleteNode method is used to delete a docker node under the FMZ Quant Trading Platform account corresponding to the API KEY in the request. The docker node ID to be deleted is specified by the nid parameter.

Returns

json
{ "code":0, "data":{ "result":true, "error":null } }
  • result: Whether the associated docker program was successfully deleted.

Arguments

NameTypeRequiredDescription

nid

number

Yes

The nid parameter is used to specify the docker ID to be deleted. You can obtain the docker information under the account through the GetNodeList method.

The GetExchangeList method is used to get the list of exchanges supported by the FMZ quantitative trading platform and their configuration information.

Returns

When the isSummary parameter is false, the returned data:

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 } }

When the isSummary parameter is true, the returned data:

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: Exchange configuration metadata.

Arguments

NameTypeRequiredDescription

isSummary

bool

Yes

The isSummary parameter is used to specify whether the returned data is summary information.

The GetPlatformList method is used to get the list of configured exchanges under the FMZ Quant Trading Platform account corresponding to the API KEY in the request.

Returns

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: Total number of configured exchange objects.
  • platforms: Exchange related information.
    • eid: Exchange identifier on the FMZ Quant Trading Platform, eid is required in certain configurations and parameters.

Arguments

NameTypeRequiredDescription

offset

number

No

Paging offset, default 0.

limit

number

No

Page size; omitted or less than or equal to 0 returns everything.

The GetStrategyList method is used to retrieve platform strategy information.

Returns

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: Total number of strategies matching the filter criteria.
  • strategies: Detailed information of the strategies found; category and language take the values described in the parameters above.

Arguments

NameTypeRequiredDescription

offset

number

Yes

Paging offset.

length

number

Yes

Page size; less than or equal to 0 returns everything.

strategyType

number

Yes

Scope of the query:

  • -1: your own and rented strategies (official strategies included).
  • 0: your own and rented strategies (official strategies excluded).
  • -3: only your own strategies.
  • -6: only rented strategies (expired ones included).
  • -4: official strategies.
  • -2: public and paid strategies in the Strategy Square.
  • 1: published strategies.
  • 2: strategies pending review.

category

number

Yes

Strategy type:

  • -1: all.
  • 0: ordinary strategies.
  • 20: template libraries.
  • 21: trading plugins.

language

number

Yes

Programming language of the strategy:

  • -1: all languages.
  • 0: JavaScript (TypeScript strategies are stored as JavaScript with a //@ts-check line in the source).
  • 1: Python.
  • 3: Blockly.
  • 4: MyLanguage.
  • 5: PINE.
  • 6: Workflow.
  • 7: Rust.

kw

string

Yes

Keywords matched against strategy names, separated by spaces; an empty string means no filter. Starting with id: queries by strategy ID, e.g. id:123,456.

groupId

number

No

Strategy group: -1 all (default), 0 ungrouped, greater than 0 a specific group. Only applies to your own strategies.

orderBy

string

No

Sort field: name, last_modified, date, optionally followed by asc for ascending order (default descending); an empty string keeps the default order.

Remarks

There is no needArgs parameter. Passing an extra parameter after category, as older documentation did, shifts all following parameters; pass them in the order above, or by name:

plaintext
api('GetStrategyList', 0, 10, -3, -1, -1, '') # first 10 of your own strategies api('GetStrategyList', strategyType=-3, language=7) # all of your own Rust strategies

The GetRobotGroupList method is used to get the list of live trading groups under the FMZ Quant Trading Platform account corresponding to the API KEY in the request.

Returns

json
{ "code": 0, "data": { "result": { "items": [{ "id": 3417, "name": "Test" }, { "id": 3608, "name": "Live Trading Demo" }] }, "error": null } }
  • items: Live trading group information.
    • id: Live trading group ID.
    • name: Live trading group name.

The items field only records newly created groups, the "Default" group is not included in items.

Arguments

No parameters

The GetRobotList method returns the live trading bots of the platform account of the API KEY in the request. All parameters are optional.

Returns

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": "Test", "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": "Test", "strategy_public": 0, "uid": "105ed6e511cc977921610fdbb7e2a1d6", "wd": 0 }] }, "error": null } }
  • all: Total number of bots matching the filters.
  • robots: Live trading bot information; status is the live trading status code.
    • group_id: Live trading bot group ID; if the live trading bot is in the default group, the group_id field is not included.

Arguments

NameTypeRequiredDescription

offset

number

No

Paging offset, default 0.

length

number

No

Page size; less than or equal to 0 returns everything (default).

customStatus

number

No

Filter by live trading status code, see Live Trading Status Codes; -1 returns all bots (default), -2 returns all bots sorted by start time.

appId

string

No

Filter by the bot's custom label (appid in settings when it was created); an empty string means no filter.

kw

string

No

Keyword matched against bot names; an empty string means no filter.

groupId

number

No

Live trading group: -1 all (default), 0 ungrouped, greater than 0 a specific group.

orderBy

string

No

Sort field: name, status, node, profit, date, refresh, start_time, strategy_name, optionally followed by asc for ascending order (default descending); an empty string keeps the default order.

strategyId

number

No

When greater than 0, only bots of this strategy are returned; default 0 (no filter).

Remarks

Using api() from the Python example on the signature authentication page:

  • api('GetRobotList'): all live trading bots.
  • api('GetRobotList', 'member2'): a single string is taken as the label; all bots labeled member2.
  • api('GetRobotList', 0, 100, -1, 'member2', ''): positional parameters; up to 100 bots labeled member2, starting at offset 0.
  • api('GetRobotList', appId='member2', length=100): the same by parameter name.

The GetRobotDetail method is used to get detailed information of a live trading bot under the FMZ Quant Trading Platform account corresponding to the API KEY in the request. The detailed information of the live trading bot to be retrieved is specified by the robotId parameter.

Returns

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": "Test", "node_id": 123, "pexchanges": { "123": "Futures_OKX" }, "phash": { "123": "ca1aca74b9cf7d8624f2af2dac01e36d" }, "plabels": { "123": "OKX Futures" }, "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": "Test", "strategy_public": "0", "uid": "105ed6e51bcc17792a610fdbb7e2a1d6", "username": "abc", "wd": 0 } }, "error": null } }
  • charge_time: Next billing time (Unix timestamp in seconds), i.e. the end of the period already paid for.
  • charged: Total billed time in seconds.
  • consumed: Total amount charged in USD, stored as an integer scaled by 1e8; 5375000000 in the example is 53.75 USD.
  • date: Creation date.
  • fixed_id: Docker ID assigned during live trading. If auto-assigned, this value is -1.
  • is_manager: Whether has permission to manage this live trading bot.
  • is_sandbox: Whether it is a simulated trading bot.
  • name: Live trading bot name.
  • node_id: Docker ID.
  • pexchanges: Exchange objects configured for the live trading bot, where 123 is the pid and "Futures_OKX" is the exchange ID (eid).
  • plabels: Label information for the exchange objects configured for the live trading bot.
  • profit: Live trading bot profit data.
  • public: Whether the live trading bot is public.
  • refresh: Last active time.
  • strategy_exchange_pairs: Configured exchange objects and their trading pair information.
  • wd: Whether offline alert is enabled.

Arguments

NameTypeRequiredDescription

robotId

number

Yes

The robotId parameter is used to specify the ID of the live trading bot for which to retrieve detailed information. The live trading bot information under the account, including the bot ID, can be obtained through the GetRobotList method.

Remarks

Explanation of the strategy_exchange_pairs attribute, using the following data as an example:

plaintext
"[60,[44314,42960,15445,14703],[\"BTC_USDT\",\"BTC_USDT\",\"ETH_USDT\",\"ETH_USDT\"]]"

The first data 60 indicates that the default K-line period set for the live trading bot is 1 minute, i.e., 60 seconds.

[44314,42960,15445,14703] are the pid values of the exchange objects configured for the live trading bot (arranged in the order they were added).

[\"BTC_USDT\",\"BTC_USDT\",\"ETH_USDT\",\"ETH_USDT\"] are the trading pairs set for the exchange objects configured for the live trading bot (corresponding one-to-one with the pid values in the order they were added).

The GetRobotLogs method is used to get the live trading log information under the FMZ Quant Trading Platform account corresponding to the API KEY in the request. The live trading ID for which to get log information is specified by the robotId parameter.

Returns

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: Log information; the queried log data entries are stored in the Arr field.
    The first data structure in logs contains log records from the strategy log table in the live trading database.
    The second data structure in logs contains log records from the profit log table in the live trading database.
    The third data structure in logs contains log records from the chart log table in the live trading database.
  • summary: Live trading status bar data.

Arguments

NameTypeRequiredDescription

robotId

number

Yes

The robotId parameter is used to specify the live trading ID for which to get log information. You can use the GetRobotList method to get the live trading information under the account, which includes the live trading ID.

logMinId

number

Yes

The logMinId parameter is used to specify the minimum ID of log records.

logMaxId

number

Yes

The logMaxId parameter is used to specify the maximum ID of log records.

logOffset

number

Yes

The logOffset parameter is used to set the offset. Within the range determined by logMinId and logMaxId, skip the specified number of records according to logOffset to determine the starting position for data retrieval.

logLimit

number

Yes

The logLimit parameter is used to set the number of data records to retrieve starting from the initial position.

profitMinId

number

Yes

The profitMinId parameter is used to set the minimum ID of profit logs.

profitMaxId

number

Yes

The profitMaxId parameter is used to set the maximum ID of profit logs.

profitOffset

number

Yes

The profitOffset parameter is used to set the offset, i.e., skip the specified number of records as the starting position.

profitLimit

number

Yes

The profitLimit parameter is used to set the number of data records to retrieve starting from the initial position.

chartMinId

number

Yes

The chartMinId parameter is used to set the minimum ID of chart data records.

chartMaxId

number

Yes

The chartMaxId parameter is used to set the maximum ID of chart data records.

chartOffset

number

Yes

The chartOffset parameter is used to set the offset.

chartLimit

number

Yes

The chartLimit parameter is used to set the number of records to retrieve.

chartUpdateBaseId

number

Yes

The chartUpdateBaseId parameter is used to set the base ID for querying update records.

chartUpdateDate

number

Yes

The chartUpdateDate parameter is used to set the update timestamp of data records, and the system will filter out records greater than this timestamp.

summaryLimit

number

Yes

The summaryLimit parameter is used to set the number of bytes of status bar data to query. This parameter is an integer used to query the status bar data of live trading.

Setting it to 0 means not querying status bar information; setting it to a non-zero value indicates the number of bytes of status bar information to query (this interface does not limit the amount of data, you can specify a larger summaryLimit parameter to get all status bar information). The status bar data is stored in the summary field of the returned data.

logExchange

string

No

Only logs of this exchange object (by label); an empty string means no filter.

logKeyword

string

No

Only logs whose content contains this keyword; an empty string means no filter.

logTypes

string

No

Only logs of these types, as comma-separated log type numbers, e.g. "0,1,2" for buy, sell and cancel logs; an empty string means all types.

Remarks

  • Strategy log table in database
    The description of the Arr attribute value in the first element (log data) of the logs attribute value (array structure) in the returned data is as follows:

    plaintext
    "Arr": [ [3977, 3, "Futures_OKX", "", 0, 0, "Sell(688.9, 2): 20016", 1526954372591, "", ""], [3976, 5, "", "", 0, 0, "this_week Position too large, long: 2", 1526954372410, "", ""] ],
    idlogTypeeidorderIdpriceamountextradatecontractTypedirection
    39773"Futures_OKX"""00"Sell(688.9, 2): 20016"1526954372591""""
    39765""""00"this_week Position too large, long: 2"1526954372410""""

    extra is the additional information for the printed log.

    The log type descriptions corresponding to logType values are as follows:

    logType:0123456
    logType meaning:BUYSALERETRACTERRORPROFITMESSAGERESTART
    English meaningBuy order logSell order logCancel orderErrorProfitMessageRestart
  • Profit chart log table in database
    The data in this chart log table is consistent with the profit logs in the strategy log table.

    plaintext
    "Arr": [ [202, 2515.44, 1575896700315], [201, 1415.44, 1575896341568] ]

    Taking one log data as an example:

    plaintext
    [202, 2515.44, 1575896700315]

    202 is the log ID, 2515.44 is the profit value, 1575896700315 is the timestamp.

  • Chart log table in database

    plaintext
    "Arr": [ [23637, 0, "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"], [23636, 5, "{\"x\":1575960300000,\"y\":3.0735}"] ]

    Taking one log data as an example:

    plaintext
    [23637, 0, "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"],

    23637 is the log ID, 0 is the chart data series index, and the final data "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}" is the log data, which is the K-line data on the chart.

The NewRobot method creates a live trading bot under the platform account of the API KEY in the request and starts it; like creating a bot on the website, this charges fees.

Returns

json
{ "code":0, "data":{ "result":591988, "error":null } }
  • result: The ID of the new bot on success; a negative number on failure, with the meaning of the abnormal codes in Live Trading Status Codes (e.g. -2 no docker found, -5 insufficient balance).

Arguments

NameTypeRequiredDescription

settings

JSON object

Yes

Live trading configuration; for its fields see "Live trading configuration (settings)" in Extended API Interface Details. For example:

json
{ "name": "test", "strategy": 123, "args": [], "exchanges": [ {"pid": 123, "pair": "SOL_USDT"} ], "period": 60, "node": 123, "group": 123, "appid": "test" }

Remarks

When the exchange configuration is passed directly with eid, the platform does not store the keys in meta, so every later RestartRobot of this bot must pass settings again.

The RestartRobot method starts (restarts) a live trading bot of the platform account of the API KEY in the request; the bot is given by robotId. Starting a bot charges fees.

Returns

json
{ "code":0, "data":{ "result":1, "error":null } }
  • result: Live trading status code, 1 indicates running.

Arguments

NameTypeRequiredDescription

robotId

number

Yes

Live trading bot ID, see the GetRobotList method.

settings

JSON object

No

Live trading configuration; for its fields see "Live trading configuration (settings)" in Extended API Interface Details. When given, the bot's configuration (name, parameters, exchanges, K-line period, docker, group) is updated with it before starting; the strategy cannot be changed.

Remarks

A bot created on the website with exchange accounts referenced by pid can be started with robotId alone, using its current configuration. A bot whose exchanges were passed directly with eid (usually created through the extended API) has no stored keys, so settings must be passed on every restart.

The StopRobot method is used to stop a live trading bot under the FMZ Quant Trading Platform account corresponding to the API KEY in the request. The bot Id to be stopped is specified by the robotId parameter.

Returns

json
{ "code":0, "data":{ "result":2, "error":null } }
  • result: Bot status code, 2 indicates stopping.

Arguments

NameTypeRequiredDescription

robotId

number

Yes

The robotId parameter is used to specify the bot Id to be stopped. You can obtain the bot information under the account through the GetRobotList method, which includes the bot Id.

The CommandRobot method is used to send interactive commands to a live trading bot under the FMZ Quant Trading Platform account corresponding to the API KEY in the request. The bot Id that receives the interactive command is specified by the robotId parameter, and the interactive command is captured and returned by the GetCommand() function called in the strategy.

Returns

json
{ "code":0, "data":{ "result":true, "error":null } }
  • result: Whether the interactive command was sent successfully. When sending a command to a bot that is not running, the result in the returned data will be false.

Arguments

NameTypeRequiredDescription

robotId

number

Yes

The robotId parameter is used to specify the bot Id that receives the interactive command. You can use the GetRobotList method to get information about bots under the account, which includes the bot Id.

cmd

string

Yes

The interactive command sent to the bot; the strategy reads it with GetCommand(), see GetCommand.

Remarks

Example of bot strategy (assuming this strategy bot is running with bot Id 123):

javascript
function main() { while (true) { var cmd = GetCommand() if (cmd) { Log(cmd) } Sleep(2000) } }

Calling api("CommandRobot", 123, "test command") with api() from the Python example on the signature authentication page, the bot with Id 123 will receive the interactive command: test command, and output it through the Log function.

The DeleteRobot method deletes a live trading bot of the platform account of the API KEY in the request; the bot is given by robotId. A running bot must be stopped first. Deletion cannot be undone.

Returns

json
{ "code":0, "data":{ "result":0, "error":null } }
  • result: Result of the deletion.
    • 0: deleted.
    • -1: not deleted: the bot does not exist, or it is still running, starting or stopping.
    • -2: the bot was deleted, but its docker could not be reached, so the log data was not removed; delete it manually under logs/storage/<bot ID>/ in the docker's directory (e.g. 123.db3).

Arguments

NameTypeRequiredDescription

robotId

number

Yes

ID of the bot to delete, see the GetRobotList method.

removeLog

bool

No

Whether to delete the bot's log data on the docker as well; default true.

The PluginRun method runs a piece of JavaScript code on a docker and returns the result. It uses the same execution mechanism as the "Debug Tool" among the development tools and trading terminal plugins (see Integrations → Trading Terminal → Plugin Principle and Development). No live trading bot is created and nothing is charged; one run lasts at most 5 minutes.

Returns

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: The result as a JSON string: logs holds the logs written with Log(), result the JSON text of the value returned by main().

Arguments

NameTypeRequiredDescription

settings

JSON object

Yes

Run configuration, for example:

json
{ "source": "function main() {Log(\"Hello FMZ\")}", "node": 123, "period": 60, "exchanges": [{"pid": 123, "pair": "SOL_USDT"}] }
  • source: the code to run. The entry point is main(), whose return value is the result.
  • strategy: when source is not given, run the account's strategy with this ID (e.g. a trading plugin).
  • node: ID of the docker that runs the code; omitted or -1 selects one automatically.
  • exchanges: exchange object configuration, same as "Live trading configuration (settings)" in Extended API Interface Details.

Remarks

exchanges can also pass the exchange configuration directly instead of referencing an exchange account on the platform, for example:

plaintext
{"eid": "Binance", "pair": "ETH_BTC", "meta": {"AccessKey": "...", "SecretKey": "..."}}

The field names of meta are given by the meta returned by GetExchangeList. Usually only one exchange object is set in exchanges (the debug tool page also supports only one); setting two causes no error, but accessing the second exchange object in the code does.

The extended API returns this structure:

json
{ "code": 0, "data": { "result": null, "error": null } }

code is the status of the request itself:

DescriptionCode
Success0
Invalid API KEY: the AccessKey does not exist or is disabled; or a wrong secret_key in direct verification1
Invalid signature2
Nonce error: the nonce is not greater than the previous one, or differs from server time by more than 1 hour3
Invalid method: the method does not exist, is not public, or this API KEY has no permission for it4
Invalid arguments: args is not valid JSON, or the call failed5
Internal error6
The request's source IP is not in this API KEY's IP whitelist7

code 0 only means the request was accepted. The method's result is in data.result; when the method fails, data.error holds the error message (null on success). For example, with the wrong number of arguments:

json
{ "code": 0, "data": { "result": null, "error": "Params number mismatch for StopRobot: expected 1, got 0" } }

The status field in the data returned by GetRobotList, GetRobotDetail, and GetRobotLogs interfaces represents: Live Trading Status Code.

  • Normal Start
    StatusCode
    Idle0
    Running1
    Stopping2
    Exited3
    Stopped4
    Strategy Error5
  • Exception
    StatusCode
    Strategy expired, please contact author to repurchase-1
    Docker not found-2
    Strategy compilation error-3
    Live trading already running-4
    Insufficient balance-5
    Strategy concurrency limit exceeded-6

The platform provides a modular, customizable Trading Terminal page: add market data, trading and other modules freely, drag and resize them, change the exchange and trading pair a module is bound to, and add several modules of the same kind, which makes manual and semi-automated trading convenient.

The trading terminal also supports trading plugins: code you write yourself that runs as a module on a selected docker to assist manual trading.

How it works

A trading plugin is a short piece of code executed on a docker: when you click "Execute" on the trading terminal page, the platform sends the plugin code and the exchange account selected in the module to the selected docker, runs it, and shows the return value in the module. The following entry points share the same execution mechanism:

Entry pointCode that runsNotes
Trading terminal pluginA strategy of type "Trading Plugin" in your strategy libraryAdded and executed on the trading terminal page
Debug Tool (development tools)JavaScript code written on the pageFor testing API calls
Extended API PluginRunsource in the request, or an existing strategy of the accountFor programs
MCP plugin_* toolsBuilt-in functions for market data, orders and so onFor AI assistants, authorized by the trade permission, see Integrations → AI Integration

No live trading bot is created and nothing is charged; one run lasts at most 5 minutes and is interrupted on timeout. It suits simple tasks that assist manual trading, such as iceberg orders, placing or cancelling orders in bulk, or calculations; logic that has to run for a long time should be a live trading bot.

Writing a plugin

Create a strategy and set its type to "Trading Plugin" on the new-strategy page. Trading plugins, the debugging tool and PluginRun support JavaScript only.

The plugin's entry point is main(), and its return value is the result: a returned table or chart object is shown as a table or chart in the module (see Integrations → Trading Terminal → Plugin Examples). Logs written with Log() are not shown in the module.

Using a plugin

  • Add: open the module menu on the trading terminal page; the trading plugins in your strategy library are listed there. Choose one to add it.
  • Execute: click "Execute" in the plugin module to run it.

Data directory

When plugins and the debug tool run on a docker, their working directory is logs/storage/p<number>/ under the docker's running directory (one directory starting with p per platform account, created on the first run). If an exchange account used by the trading terminal configures its key as a key file path (file:///xxx.txt), put the key file in this directory.

A plugin runs code for a limited time to do simple jobs such as iceberg orders, placing and cancelling orders, or calculations. It returns its result with return; a returned table or chart object is displayed as a table or chart. Two examples follow; more can be found in the Strategy Square, e.g. buying or selling in small slices.

Order book snapshot

Show the top 15 levels of the current order book as a table:

javascript
// Return an order book snapshot function main() { var tbl = { type: 'table', title: 'Depth snapshot @ ' + _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 }

Calendar spread chart

On a futures exchange object, take 5-minute K-lines of the quarterly and the weekly contract and chart the difference of their closes:

javascript
// Chart the calendar spread var chart = { __isStock: true, title: {text: 'Spread analysis'}, xAxis: {type: 'datetime'}, yAxis: { title: {text: 'Spread'}, 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 }