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

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: