Global
Version
Returns the current system version number.
Version()Examples
javascript
function main() {
Log("version:", Version())
}
python
def main():
Log("version:", Version())
c++
void main() {
Log("version:", Version());
}Returns
| Type | Description |
string | Current system version number, for example: |
Remarks
The system version number is the version number of the docker program.
Sleep
Sleep function, used to pause program execution for a specified time.
Sleep(millisecond)Examples
javascript
function main() {
Sleep(1000 * 10) // Wait for 10 seconds
Log("Waited for 10 seconds")
}
python
def main():
Sleep(1000 * 10)
Log("Waited for 10 seconds")
c++
void main() {
Sleep(1000 * 10);
Log("Waited for 10 seconds");
}Arguments
| Name | Type | Required | Description |
millisecond | number | Yes | The |
Remarks
For example, when executing Sleep(1000) function, the program will sleep for 1 second. Sleep times less than 1 millisecond are supported, such as setting Sleep(0.1). The minimum supported parameter value is 0.000001, which is nanosecond-level sleep, where 1 nanosecond equals 1e-6 milliseconds.
When writing strategies in Python, the Sleep(millisecond) function should be used for operations such as polling intervals and time waiting. It is not recommended to use the time.sleep(second) function from Python's time library. This is because using time.sleep(second) in a strategy will cause the strategy program to actually wait for the corresponding time during backtesting (rather than skipping ahead in the backtest system's time sequence), resulting in extremely slow strategy backtesting.
IsVirtual
Determine whether the strategy's runtime environment is a backtesting system.
IsVirtual()Examples
javascript
function main() {
if (IsVirtual()) {
Log("Currently in backtest environment.")
} else {
Log("Currently in live trading environment.")
}
}
python
def main():
if IsVirtual():
Log("Currently in backtest environment.")
else:
Log("Currently in live trading environment.")
c++
void main() {
if (IsVirtual()) {
Log("Currently in backtest environment.");
} else {
Log("Currently in live trading environment.");
}
}Returns
| Type | Description |
bool | Returns true when the strategy is running in a backtesting system environment, for example: |
Remarks
Determine whether the current runtime environment is a backtesting system, used to handle differences between backtesting and live trading environments.
Send email.
Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)Examples
javascript
function main(){
Mail("smtp.163.com", "[email protected]", "password", "[email protected]", "title", "body")
}
python
def main():
Mail("smtp.163.com", "[email protected]", "password", "[email protected]", "title", "body")
c++
void main() {
Mail("smtp.163.com", "[email protected]", "password", "[email protected]", "title", "body");
}Returns
| Type | Description |
bool | Returns |
Arguments
| Name | Type | Required | Description |
smtpServer | string | Yes | Specify the |
smtpUsername | string | Yes | Specify the email address of the email sender. |
smtpPassword | string | Yes | The |
mailTo | string | Yes | Specify the email address of the email recipient. |
title | string | Yes | Email subject. |
body | string | Yes | Email body content. |
See Also
Remarks
The smtpPassword parameter is set to the SMTP service password, not the email login password.
When setting the smtpServer parameter, if you need to change the port, you can directly specify the port number in the smtpServer parameter. For example: QQ Mail smtp.qq.com:587 (this port has been tested and is available).
If an error message appears: unencryped connection, you need to modify the smtpServer parameter of the Mail function. The parameter format is: ssl://xxx.com:xxx, for example, QQ Mail's SMTP SSL method: ssl://smtp.qq.com:465, or use the format smtp://xxx.com:xxx.
This function does not work in the backtesting system.
Mail_Go
Asynchronous version of the Mail function.
Mail_Go(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)Examples
javascript
function main() {
var r1 = Mail_Go("smtp.163.com", "[email protected]", "password", "[email protected]", "title", "body")
var r2 = Mail_Go("smtp.163.com", "[email protected]", "password", "[email protected]", "title", "body")
var ret1 = r1.wait()
var ret2 = r2.wait()
Log("ret1:", ret1)
Log("ret2:", ret2)
}
python
# Not supported
c++
// Not supportedReturns
| Type | Description |
object | The |
Arguments
| Name | Type | Required | Description |
smtpServer | string | Yes | Used to specify the |
smtpUsername | string | Yes | Used to specify the email address of the email sender. |
smtpPassword | string | Yes | The |
mailTo | string | Yes | Used to specify the email address of the email recipient. |
title | string | Yes | Email subject. |
body | string | Yes | Email body content. |
See Also
Remarks
Does not work in the backtesting system.
SetErrorFilter
Filter error logs.
SetErrorFilter(filters)Examples
-
Filter common errors.
javascriptfunction main() { SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused") }pythondef main(): SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")c++void main() { SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused"); } -
Filter specific API error messages.
javascriptfunction main() { // Query a non-existent order with id 123, intentionally causing an API error var order = exchange.GetOrder("123") Log(order) // Filter http502 errors and GetOrder API errors. After setting error filter, the second call to GetOrder will not report an error SetErrorFilter("502:|GetOrder") order = exchange.GetOrder("123") Log(order) }pythondef main(): order = exchange.GetOrder("123") Log(order) SetErrorFilter("502:|GetOrder") order = exchange.GetOrder("123") Log(order)c++void main() { TId orderId; Order order = exchange.GetOrder(orderId); Log(order); SetErrorFilter("502:|GetOrder"); order = exchange.GetOrder(orderId); Log(order); }
Arguments
| Name | Type | Required | Description |
filters | string | Yes | Regular expression string. |
Remarks
Error logs matched by this regular expression will not be uploaded to the log system. Can be called multiple times (no limit) to set multiple filter conditions, and the regular expressions set multiple times will accumulate and take effect. You can set an empty string to reset the error log filter regular expression: SetErrorFilter(""). Filtered logs will no longer be written to the database file of the corresponding live trading ID in the docker directory, preventing frequent errors from causing database file bloat.
GetPid
Get the unique identifier of the live trading process.
GetPid()Examples
javascript
function main(){
var id = GetPid()
Log(id)
}
python
def main():
id = GetPid()
Log(id)
c++
void main() {
auto id = GetPid();
Log(id);
}Returns
| Type | Description |
string | Returns the unique identifier of the current live trading process. |
GetLastError
Get the most recent error message.
GetLastError()Examples
javascript
function main(){
// 因为不存在编号为123的订单,所以会产生错误
exchange.GetOrder("123")
var error = GetLastError()
Log(error)
}
python
def main():
exchange.GetOrder("123")
error = GetLastError()
Log(error)
c++
void main() {
// 订单ID类型:TId,因此不能传入字符串,我们下一个不符合交易所规范的订单来触发错误
exchange.GetOrder(exchange.Buy(1, 1));
auto error = GetLastError();
Log(error);
}Returns
| Type | Description |
string | The most recent error message. |
Remarks
Does not work in the backtesting system.
GetCommand
Get strategy interaction commands.
GetCommand()Examples
-
Detect interactive commands and use the
Logfunction to output the command when an interactive command is detected.javascriptfunction main(){ while(true) { var cmd = GetCommand() if (cmd) { Log(cmd) } Sleep(1000) } }pythondef main(): while True: cmd = GetCommand() if cmd: Log(cmd) Sleep(1000)c++void main() { while(true) { auto cmd = GetCommand(); if(cmd != "") { Log(cmd); } Sleep(1000); } } -
For example, add a control without an input box in the strategy interaction controls, name the interaction control:
buy, control description:Buy, this is a button control. Continue to add a control with an input box, name the interaction control:sell, control description:Sell, this is an interaction control composed of a button and an input box. Design interaction code in the strategy to respond to different interaction controls:javascriptfunction main() { while (true) { LogStatus(_D()) var cmd = GetCommand() if (cmd) { Log("cmd:", cmd) var arr = cmd.split(":") if (arr[0] == "buy") { Log("Buy, this control has no quantity") } else if (arr[0] == "sell") { Log("Sell, this control has quantity:", arr[1]) } else { Log("Other control triggered:", arr) } } Sleep(1000) } }pythondef main(): while True: LogStatus(_D()) cmd = GetCommand() if cmd: Log("cmd:", cmd) arr = cmd.split(":") if arr[0] == "buy": Log("Buy, this control has no quantity") elif arr[0] == "sell": Log("Sell, this control has quantity:", arr[1]) else: Log("Other control triggered:", arr) Sleep(1000)c++#include <iostream> #include <sstream> #include <string> #include <vector> using namespace std; void split(const string& s,vector<string>& sv,const char flag = ' ') { sv.clear(); istringstream iss(s); string temp; while (getline(iss, temp, flag)) { sv.push_back(temp); } return; } void main() { while(true) { LogStatus(_D()); auto cmd = GetCommand(); if (cmd != "") { vector<string> arr; split(cmd, arr, ':'); if(arr[0] == "buy") { Log("Buy, this control has no quantity"); } else if (arr[0] == "sell") { Log("Sell, this control has quantity:", arr[1]); } else { Log("Other control triggered:", arr); } } Sleep(1000); } }
Returns
| Type | Description |
string | The returned command format is |
Remarks
Not effective in the backtesting system.
GetMeta
Get the Meta value written when generating the strategy registration code.
GetMeta()Examples
Application scenario example: Using Meta to limit the asset quantity that a strategy can operate.
javascript
function main() {
// Maximum quote currency asset value allowed by the strategy
var maxBaseCurrency = null
// Get metadata from when the registration code was created
var level = GetMeta()
// Check conditions corresponding to Meta
if (level == "level1") {
// -1 means no limit
maxBaseCurrency = -1
} else if (level == "level2") {
maxBaseCurrency = 10
} else if (level == "level3") {
maxBaseCurrency = 1
} else {
maxBaseCurrency = 0.5
}
while(1) {
Sleep(1000)
var ticker = exchange.GetTicker()
// Check asset value
var acc = exchange.GetAccount()
if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
// Stop executing strategy trading logic
LogStatus(_D(), "level:", level, "Position exceeds registration code limit, strategy trading logic will not execute!")
continue
}
// Other trading logic
// Normal status bar information output
LogStatus(_D(), "level:", level, "Strategy running normally! ticker data:\n", ticker)
}
}
python
def main():
maxBaseCurrency = null
level = GetMeta()
if level == "level1":
maxBaseCurrency = -1
elif level == "level2":
maxBaseCurrency = 10
elif level == "level3":
maxBaseCurrency = 1
else:
maxBaseCurrency = 0.5
while True:
Sleep(1000)
ticker = exchange.GetTicker()
acc = exchange.GetAccount()
if maxBaseCurrency != -1 and maxBaseCurrency < acc["Stocks"] + acc["FrozenStocks"]:
LogStatus(_D(), "level:", level, "Position exceeds registration code limit, strategy trading logic will not execute!")
continue
# Other trading logic
# Normal status bar information output
LogStatus(_D(), "level:", level, "Strategy running normally! ticker data:\n", ticker)
c++
void main() {
auto maxBaseCurrency = 0.0;
auto level = GetMeta();
if (level == "level1") {
maxBaseCurrency = -1;
} else if (level == "level2") {
maxBaseCurrency = 10;
} else if (level == "level3") {
maxBaseCurrency = 1;
} else {
maxBaseCurrency = 0.5;
}
while(1) {
Sleep(1000);
auto ticker = exchange.GetTicker();
auto acc = exchange.GetAccount();
if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
// Stop executing strategy trading logic
LogStatus(_D(), "level:", level, "Position exceeds registration code limit, strategy trading logic will not execute!");
continue;
}
// Other trading logic
// Normal status bar information output
LogStatus(_D(), "level:", level, "Strategy running normally! ticker data:\n", ticker);
}
}Returns
| Type | Description |
string |
|
Remarks
Application scenario: When you need to impose capital limits on different strategy renters. The Meta value set when generating the registration code cannot exceed 190 characters. The GetMeta() function only supports live trading. If no metadata (Meta) was set when generating the strategy registration code, the GetMeta() function returns null. This function does not work in the backtesting system.
Dial
Used for raw Socket access, supporting tcp, udp, tls, unix protocols. Supports 4 popular communication protocols: mqtt, nats, amqp, kafka. Supports database connections, including: sqlite3, mysql, postgres, clickhouse.
Dial(address)
Dial(address, timeout)
Dial(address, options)Examples
-
Dial function call example:
javascriptfunction main(){ // Dial supports tcp://, udp://, tls://, unix:// protocols, can add a parameter to specify timeout in seconds var client = Dial("tls://www.baidu.com:443") if (client) { // write can append a numeric parameter to specify timeout, returns the number of bytes successfully sent client.write("GET / HTTP/1.1\nConnection: Closed\n\n") while (true) { // read can append a numeric parameter to specify timeout, unit: milliseconds. Returns null indicating error, timeout, or socket closed var buf = client.read() if (!buf) { break } Log(buf) } client.close() } }pythondef main(): client = Dial("tls://www.baidu.com:443") if client: client.write("GET / HTTP/1.1\nConnection: Closed\n\n") while True: buf = client.read() if not buf: break Log(buf) client.close()c++void main() { auto client = Dial("tls://www.baidu.com:443"); if(client.Valid) { client.write("GET / HTTP/1.1\nConnection: Closed\n\n"); while(true) { auto buf = client.read(); if(buf == "") { break; } Log(buf); } client.close(); } } -
Accessing Binance WebSocket market data interface:
javascriptfunction main() { LogStatus("Connecting...") // Access Binance WebSocket interface var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr") if (!client) { Log("Connection failed, exiting") return } while (true) { // read only returns data obtained after calling read var buf = client.read() if (!buf) { break } var table = { type: 'table', title: 'Market Chart', cols: ['Symbol', 'High', 'Low', 'Bid', 'Ask', 'Last Price', 'Volume', 'Update Time'], rows: [] } var obj = JSON.parse(buf) _.each(obj, function(ticker) { table.rows.push([ticker.s, ticker.h, ticker.l, ticker.b, ticker.a, ticker.c, ticker.q, _D(ticker.E)]) }) LogStatus('`' + JSON.stringify(table) + '`') } client.close() }pythonimport json def main(): LogStatus("Connecting...") client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr") if not client: Log("Connection failed, exiting") return while True: buf = client.read() if not buf: break table = { "type" : "table", "title" : "Market Chart", "cols" : ["Symbol", "High", "Low", "Bid", "Ask", "Last Price", "Volume", "Update Time"], "rows" : [] } obj = json.loads(buf) for i in range(len(obj)): table["rows"].append([obj[i]["s"], obj[i]["h"], obj[i]["l"], obj[i]["b"], obj[i]["a"], obj[i]["c"], obj[i]["q"], _D(int(obj[i]["E"]))]) LogStatus('`' + json.dumps(table) + '`') client.close()c++void main() { LogStatus("Connecting..."); auto client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr"); if(!client.Valid) { Log("Connection failed, exiting"); return; } while(true) { auto buf = client.read(); if(buf == "") { break; } json table = R"({ "type" : "table", "title" : "Market Chart", "cols" : ["Symbol", "High", "Low", "Bid", "Ask", "Last Price", "Volume", "Update Time"], "rows" : [] })"_json; json obj = json::parse(buf); for(auto& ele : obj.items()) { table["rows"].push_back({ele.value()["s"], ele.value()["h"], ele.value()["l"], ele.value()["b"], ele.value()["a"], ele.value()["c"], ele.value()["q"], _D(ele.value()["E"])}); } LogStatus("`" + table.dump() + "`"); } client.close(); } -
Access Binance's WebSocket interface and set wss request headers.
javascriptfunction main() { let options = {"headers": {"X-MBX-APIKEY": "your access key"}} let random = `fmz${UnixNano()}` let ts = new Date().getTime() let secretKey = "your secret key" let topic = "com_announcement_en" let payload = `random=${random}&topic=${topic}&recvWindow=30000×tamp=${ts}` let signature = Encode("sha256", "string", "hex", payload, "string", secretKey) let query = `?${payload}&signature=${signature}` Log("query:", query) let conn = Dial(`wss://api.binance.com/sapi/wss${query}`, options) for (var i = 0 ; i < 10 ; i++) { let ret = conn.read() Log(ret) } }pythonimport time def main(): options = {"headers": {"X-MBX-APIKEY": "your access key"}} random = "fmz" + str(UnixNano()) ts = int(time.time() * 1000) secretKey = "your secret key" topic = "com_announcement_en" payload = f"random={random}&topic={topic}&recvWindow=30000×tamp={ts}" signature = Encode("sha256", "string", "hex", payload, "string", secretKey) query = f"?{payload}&signature={signature}" Log("query:", query) conn = Dial(f"wss://api.binance.com/sapi/wss{query}", options) for i in range(10): ret = conn.read() Log(ret)c++// Not supported currently -
Accessing OKX WebSocket market data interface:
javascriptvar ws = null function main(){ var param = { "op": "subscribe", "args": [{ "channel": "tickers", "instId": "BTC-USDT" }] } // When calling the Dial function, specify reconnect=true to set reconnection mode, and specify payload as the message to send upon reconnection. After the WebSocket connection is disconnected, it will automatically reconnect and automatically send the message ws = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload=" + JSON.stringify(param)) if(ws){ var pingCyc = 1000 * 20 var lastPingTime = new Date().getTime() while(true){ var nowTime = new Date().getTime() var ret = ws.read() Log("ret:", ret) if(nowTime - lastPingTime > pingCyc){ var retPing = ws.write("ping") lastPingTime = nowTime Log("Sending: ping", "#FF0000") } LogStatus("Current time:", _D()) Sleep(1000) } } } function onexit() { ws.close() Log("Exiting") }pythonimport json import time ws = None def main(): global ws param = { "op": "subscribe", "args": [{ "channel": "tickers", "instId": "BTC-USDT" }] } ws = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload=" + json.dumps(param)) if ws: pingCyc = 1000 * 20 lastPingTime = time.time() * 1000 while True: nowTime = time.time() * 1000 ret = ws.read() Log("ret:", ret) if nowTime - lastPingTime > pingCyc: retPing = ws.write("ping") lastPingTime = nowTime Log("Sending: ping", "#FF0000") LogStatus("Current time:", _D()) Sleep(1000) def onexit(): ws.close() Log("Exiting")c++auto objWS = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true"); void main() { json param = R"({ "op": "subscribe", "args": [{ "channel": "tickers", "instId": "BTC-USDT" }] })"_json; objWS.write(param.dump()); if(objWS.Valid) { uint64_t pingCyc = 1000 * 20; uint64_t lastPingTime = Unix() * 1000; while(true) { uint64_t nowTime = Unix() * 1000; auto ret = objWS.read(); Log("ret:", ret); if(nowTime - lastPingTime > pingCyc) { auto retPing = objWS.write("ping"); lastPingTime = nowTime; Log("Sending: ping", "#FF0000"); } LogStatus("Current time:", _D()); Sleep(1000); } } } void onexit() { objWS.close(); Log("Exiting"); } -
Accessing Huobi's WebSocket market data interface:
javascriptvar ws = null function main(){ var param = {"sub": "market.btcusdt.detail", "id": "id1"} ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + JSON.stringify(param)) if(ws){ while(1){ var ret = ws.read() Log("ret:", ret) // Respond to heartbeat packet try { var jsonRet = JSON.parse(ret) if(typeof(jsonRet.ping) == "number") { var strPong = JSON.stringify({"pong" : jsonRet.ping}) ws.write(strPong) Log("Responding to ping, sending pong:", strPong, "#FF0000") } } catch(e) { Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message) } LogStatus("Current time:", _D()) Sleep(1000) } } } function onexit() { ws.close() Log("Executing ws.close()") }pythonimport json ws = None def main(): global ws param = {"sub" : "market.btcusdt.detail", "id" : "id1"} ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + json.dumps(param)) if ws: while True: ret = ws.read() Log("ret:", ret) # Respond to heartbeat packet try: jsonRet = json.loads(ret) if "ping" in jsonRet and type(jsonRet["ping"]) == int: strPong = json.dumps({"pong" : jsonRet["ping"]}) ws.write(strPong) Log("Responding to ping, sending pong:", strPong, "#FF0000") except Exception as e: Log("e:", e) LogStatus("Current time:", _D()) Sleep(1000) def onexit(): ws.close() Log("Executing ws.close()")c++using namespace std; void main() { json param = R"({"sub" : "market.btcusdt.detail", "id" : "id1"})"_json; auto ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + param.dump()); if(ws.Valid) { while(true) { auto ret = ws.read(); Log("ret:", ret); // Respond to heartbeat packet try { auto jsonRet = json::parse(ret); if(jsonRet["ping"].is_number()) { json pong = R"({"pong" : 0})"_json; pong["pong"] = jsonRet["ping"]; auto strPong = pong.dump(); ws.write(strPong); Log("Responding to ping, sending pong:", strPong, "#FF0000"); } } catch(exception &e) { Log("e:", e.what()); } LogStatus("Current time:", _D()); Sleep(1000); } } } void onexit() { // ws.close(); Log("Executing ws.close()"); } -
Accessing OKX's WebSocket authentication interface:
javascriptfunction getLogin(pAccessKey, pSecretKey, pPassphrase) { // Signature function for login var ts = (new Date().getTime() / 1000).toString() var login = { "op": "login", "args":[{ "apiKey" : pAccessKey, "passphrase" : pPassphrase, "timestamp" : ts, "sign" : exchange.Encode("sha256", "string", "base64", ts + "GET" + "/users/self/verify", "string", pSecretKey) }] } return login } var client_private = null function main() { // Since the read function uses timeout settings, timeout errors need to be filtered, otherwise redundant error output will be generated SetErrorFilter("timeout") // Position channel subscription information var posSubscribe = { "op": "subscribe", "args": [{ "channel": "positions", "instType": "ANY" }] } var accessKey = "xxx" var secretKey = "xxx" var passphrase = "xxx" client_private = Dial("wss://ws.okx.com:8443/ws/v5/private") client_private.write(JSON.stringify(getLogin(accessKey, secretKey, passphrase))) Sleep(3000) // Cannot subscribe to private channels immediately after login, need to wait for server response client_private.write(JSON.stringify(posSubscribe)) if (client_private) { var lastPingTS = new Date().getTime() while (true) { var buf = client_private.read(-1) if (buf) { Log(buf) } // Detect disconnection and reconnect if (buf == "" && client_private.write(JSON.stringify(posSubscribe)) == 0) { Log("Detected disconnection, closing connection, reconnecting") client_private.close() client_private = Dial("wss://ws.okx.com:8443/ws/v5/private") client_private.write(JSON.stringify(getLogin(accessKey, secretKey, passphrase))) Sleep(3000) client_private.write(JSON.stringify(posSubscribe)) } // Send heartbeat packet var nowPingTS = new Date().getTime() if (nowPingTS - lastPingTS > 10 * 1000) { client_private.write("ping") lastPingTS = nowPingTS } } } } function onexit() { var ret = client_private.close() Log("Connection closed!", ret) }pythonimport json import time def getLogin(pAccessKey, pSecretKey, pPassphrase): ts = str(time.time()) login = { "op": "login", "args":[{ "apiKey" : pAccessKey, "passphrase" : pPassphrase, "timestamp" : ts, "sign" : exchange.Encode("sha256", "string", "base64", ts + "GET" + "/users/self/verify", "string", pSecretKey) }] } return login client_private = None def main(): global client_private SetErrorFilter("timeout") posSubscribe = { "op": "subscribe", "args": [{ "channel": "positions", "instType": "ANY" }] } accessKey = "xxx" secretKey = "xxx" passphrase = "xxx" client_private = Dial("wss://ws.okx.com:8443/ws/v5/private") client_private.write(json.dumps(getLogin(accessKey, secretKey, passphrase))) Sleep(3000) client_private.write(json.dumps(posSubscribe)) if client_private: lastPingTS = time.time() * 1000 while True: buf = client_private.read(-1) if buf: Log(buf) if buf == "" and client_private.write(json.dumps(posSubscribe)) == 0: Log("Detected disconnection, closing connection, reconnecting") ret = client_private.close() client_private = Dial("wss://ws.okx.com:8443/ws/v5/private") client_private.write(json.dumps(getLogin(accessKey, secretKey, passphrase))) Sleep(3000) client_private.write(json.dumps(posSubscribe)) nowPingTS = time.time() * 1000 if nowPingTS - lastPingTS > 10 * 1000: client_private.write("ping") lastPingTS = nowPingTS def onexit(): ret = client_private.close() Log("Connection closed!", ret)c++auto client_private = Dial("wss://ws.okx.com:8443/ws/v5/private"); json getLogin(string pAccessKey, string pSecretKey, string pPassphrase) { auto ts = std::to_string(Unix()); json login = R"({ "op": "login", "args": [{ "apiKey": "", "passphrase": "", "timestamp": "", "sign": "" }] })"_json; login["args"][0]["apiKey"] = pAccessKey; login["args"][0]["passphrase"] = pPassphrase; login["args"][0]["timestamp"] = ts; login["args"][0]["sign"] = exchange.Encode("sha256", "string", "base64", ts + "GET" + "/users/self/verify", "string", pSecretKey); return login; } void main() { SetErrorFilter("timeout"); json posSubscribe = R"({ "op": "subscribe", "args": [{ "channel": "positions", "instType": "ANY" }] })"_json; auto accessKey = "xxx"; auto secretKey = "xxx"; auto passphrase = "xxx"; client_private.write(getLogin(accessKey, secretKey, passphrase).dump()); Sleep(3000); client_private.write(posSubscribe.dump()); if (client_private.Valid) { uint64_t lastPingTS = Unix() * 1000; while (true) { auto buf = client_private.read(-1); if (buf != "") { Log(buf); } if (buf == "") { if (client_private.write(posSubscribe.dump()) == 0) { Log("Detected disconnection, closing connection, reconnecting"); client_private.close(); client_private = Dial("wss://ws.okx.com:8443/ws/v5/private"); client_private.write(getLogin(accessKey, secretKey, passphrase).dump()); Sleep(3000); client_private.write(posSubscribe.dump()); } } uint64_t nowPingTS = Unix() * 1000; if (nowPingTS - lastPingTS > 10 * 1000) { client_private.write("ping"); lastPingTS = nowPingTS; } } } } void onexit() { client_private.close(); Log("Exiting"); } -
Access CoinEx's WebSocket authentication interface:
javascriptvar conn = null function main() { var accessKey = "your accessKey" var ts = new Date().getTime() var signature = exchange.Encode("sha256", "string", "hex", String(ts), "string", "{{secretkey}}") Log("signature:", signature) var payload = { "id": 1, "method": "server.sign", "params": { "access_id": accessKey, "signed_str": signature, "timestamp": ts, } } Log(`JSON.stringify(payload):`, JSON.stringify(payload)) conn = Dial("wss://socket.coinex.com/v2/futures|compress=gzip&mode=recv&payload=" + JSON.stringify(payload)) if (!conn) { throw "stop" } Log("Dial ... ", conn.read()) // Subscribe to position updates conn.write(JSON.stringify({ "method": "position.subscribe", "params": {"market_list": ["BTCUSDT"]}, "id": 1 })) while (true) { var msg = conn.read() if (msg) { Log("msg:", msg) } } } function onexit() { conn.close() }python// Omittedc++// Omitted -
Example of accessing the MEXC exchange
Websocketinterface, subscribing to thepublic.aggre.deals.v3.api.pbchannel, and usingprotobuf.jsto decode binary data:javascriptlet strPushDataV3ApiWrapper = `syntax = "proto3"; option java_package = "com.mxc.push.common.protobuf"; option optimize_for = SPEED; option java_multiple_files = true; option java_outer_classname = "PushDataV3ApiWrapperProto"; message PublicAggreDealsV3Api { repeated PublicAggreDealsV3ApiItem deals = 1; string eventType = 2; } message PublicAggreDealsV3ApiItem { string price = 1; string quantity = 2; int32 tradeType = 3; int64 time = 4; } message PushDataV3ApiWrapper { string channel = 1; oneof body { PublicAggreDealsV3Api publicAggreDeals = 314; } optional string symbol = 3; optional string symbolId = 4; optional int64 createTime = 5; optional int64 sendTime = 6; }` let code = HttpQuery("https://cdnjs.cloudflare.com/ajax/libs/protobufjs/7.5.3/protobuf.js") let exports = {} let module = { exports } new Function("module", "exports", code)(module, exports) let protobuf = module.exports function main() { const PushDataV3ApiWrapper = protobuf.parse(strPushDataV3ApiWrapper).root.lookupType("PushDataV3ApiWrapper") var payload = { "method": "SUBSCRIPTION", "params": [ "[email protected]@100ms@BTCUSDT" ] } // proxy=socks5://x.x.x.x:xxxx var conn = Dial("wss://wbs-api.mexc.com/ws|payload=" + JSON.stringify(payload)) var data = null while (true) { var ret = conn.read() if (ret) { const uint8arrayData = new Uint8Array(ret) const message = PushDataV3ApiWrapper.decode(uint8arrayData) data = PushDataV3ApiWrapper.toObject(message, { longs: String, enums: String, bytes: String, defaults: true, arrays: true, objects: true }) Log("data:", data) } LogStatus(_D(), data) } }python# You can use the corresponding libraries in Python to implement encoding and decoding.c++// Omitted -
The connection object returned by the Dial function when connecting to a database has 2 unique method functions:
-
exec(sqlString): Used to execute SQL statements, similar to theDBExec()function. -
fd(): Thefd()function returns a handle (e.g., handle variable is handle), which can be used by other threads to reconnect (even if the object created by Dial has already executed theclose()function to close the connection). Pass the handle to theDial()function, e.g.,Dial(handle)to reuse the connection.
The following is an example of using the Dial function to connect to a
sqlite3database.javascriptvar client = null function main() { // client = Dial("sqlite3://:memory:") // Use in-memory database client = Dial("sqlite3://test1.db") // Open/connect to the database file in the docker's directory // Record the handle var sqlite3Handle = client.fd() Log("sqlite3Handle:", sqlite3Handle) // Query tables in the database var ret = client.exec("SELECT name FROM sqlite_master WHERE type='table'") Log(ret) } function onexit() { Log("Executing client.close()") client.close() }python// Not supportedc++// Not supported -
Returns
| Type | Description | ||||||||||||||||||||
object | If timeout occurs, the
For data pushed via WebSocket protocol, if the time interval between
|
Arguments
| Name | Type | Required | Description |
address | string | Yes | Request address. |
timeout | number | No | Timeout period, in seconds. |
options | object | No | Configuration options. |
Remarks
Detailed explanation of the address parameter: After the normal address wss://ws.okx.com:8443/ws/v5/public, use the | symbol as a separator. If the parameter string contains the | character, use || as the separator. The part after it is the function parameter settings, with each parameter connected by the & character.
For example, when setting both ss5 proxy and compression parameters simultaneously, you can write:
Dial("wss://ws.okx.com:8443/ws/v5/public|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv")
| Features supported by the address parameter of the Dial function | Parameter description |
|---|---|
| Parameters related to WebSocket protocol data compression: compress=parameter value | compress is the compression method, the compress parameter can be gzip_raw, gzip, etc. If the gzip method is non-standard gzip, you can use the extended method: gzip_raw |
| Parameters related to WebSocket protocol data compression: mode=parameter value | mode is the compression mode, the mode parameter can be one of three options: dual, send, recv. dual is bidirectional compression, both sending and receiving are compressed data; send is sending compressed data; recv is receiving compressed data and decompressing locally. |
| WebSocket protocol enable compression setting: enableCompression=true | Use enableCompression=false to disable this setting, not enabled by default. |
| Parameters related to WebSocket protocol underlying automatic reconnection: reconnect=parameter value | reconnect indicates whether to enable reconnection, reconnect=true means enabling reconnection. When this parameter is not set, reconnection is disabled by default. |
| Parameters related to WebSocket protocol underlying automatic reconnection: interval=parameter value | interval is the retry time interval in milliseconds. interval=10000 means the retry interval is 10 seconds, when not set the default is 1 second, i.e., interval=1000. |
| Parameters related to WebSocket protocol underlying automatic reconnection: payload=parameter value | payload is the subscription message that needs to be sent when WebSocket reconnects, for example: payload=okok. |
| Parameters related to socks5 proxy: proxy=parameter value | proxy is the ss5 proxy setting, parameter value format: socks5://name:[email protected]:1080, where name is the ss5 server username, pwd is the ss5 server login password, 1080 is the ss5 service port. |
The Dial() function only supports live trading.
When using the Dial function to connect to a database, please refer to the Go language driver project for each database for writing the connection string.
| Supported databases | Driver project | Connection String | Remarks |
|---|---|---|---|
| sqlite3 | github.com/mattn/go-sqlite3 | sqlite3://file:test.db?cache=shared&mode=memory | The sqlite3:// prefix indicates using the sqlite3 database, call example: Dial("sqlite3://test1.db") |
| mysql | github.com/go-sql-driver/mysql | mysql://username:yourpassword@tcp(localhost:3306)/yourdatabase?charset=utf8mb4 | -- |
| postgres | github.com/lib/pq | postgres://user=postgres dbname=yourdatabase sslmode=disable password=yourpassword host=localhost port=5432 | -- |
| clickhouse | github.com/ClickHouse/clickhouse-go | clickhouse://tcp://host:9000?username=username&password=yourpassword&database=youdatabase | -- |
Note that when the payload content set in the address parameter contains the character = or other special characters, it may affect the parsing of the address parameter in the Dial function. For example:
BackPack Exchange WebSocket private interface call example:
javascript
var client = null
function main() {
// base64-encoded public key of the key pair, i.e., the access key configured on FMZ
var base64ApiKey = "xxx"
var ts = String(new Date().getTime())
var data = "instruction=subscribe×tamp=" + ts + "&window=5000"
// Since signEd25519 ultimately returns base64 encoding, it may contain the character "="
var signature = signEd25519(data)
// payload may contain the character "=" after JSON encoding
payload = {
"method": "SUBSCRIBE",
"params": ["account.orderUpdate"],
"signature": [base64ApiKey, signature, ts, "5000"]
}
client = Dial("wss://ws.backpack.exchange")
client.write(JSON.stringify(payload))
if (!client) {
Log("Connection failed, exiting")
return
}
while (true) {
var buf = client.read()
Log(buf)
}
}
function onexit() {
client.close()
}
function signEd25519(data) {
return exchange.Encode("ed25519.seed", "raw", "base64", data, "base64", "{{secretkey}}")
}
The following calling method in the code works correctly:
javascript
client = Dial("wss://ws.backpack.exchange")
client.write(JSON.stringify(payload))
If written directly in payload, it will not work properly, for example:
javascript
client = Dial("wss://ws.backpack.exchange|payload=" +
JSON.stringify(payload))
Currently, only JavaScript supports using mqtt, nats, amqp, kafka communication protocols in the Dial function. The following uses JavaScript strategy code as an example to demonstrate usage examples of the four protocols mqtt, nats, amqp, kafka:
javascript
// You need to configure and deploy the proxy servers for each protocol first
// For demonstration purposes, the subscription (read operation) and publishing (write operation) of topic test_topic are both performed in this strategy
var arrConn = []
var arrName = []
function main() {
LogReset(1)
conn_nats = Dial("nats://[email protected]:4222?topic=test_topic")
conn_mqtt = Dial("mqtt://127.0.0.1:1883?topic=test_topic")
conn_amqp = Dial("amqp://q:[email protected]:5672/?queue=test_Queue")
conn_kafka = Dial("kafka://localhost:9092/test_topic")
arrConn = [conn_nats, conn_amqp, conn_mqtt, conn_kafka]
arrName = ["nats", "amqp", "mqtt", "kafka"]
while (true) {
for (var i in arrConn) {
var conn = arrConn[i]
var name = arrName[i]
// Write data
conn.write(name + ", time: " + _D() + ", test msg.")
// Read data
var readMsg = conn.read(1000)
Log(name + " readMsg: ", readMsg, "#FF0000")
}
Sleep(1000)
}
}
function onexit() {
for (var i in arrConn) {
arrConn[i].close()
Log("Closing", arrName[i], "connection")
}
}
For detailed documentation, please refer to: Exploring FMZ: Communication Protocol Practice Between Live Trading Strategies
HttpQuery
Send HTTP request.
HttpQuery(url)
HttpQuery(url, options)Examples
-
Example of accessing OKX public market API endpoints.
javascriptfunction main(){ // 一个不带参数的GET请求示例 var info = JSON.parse(HttpQuery("https://www.okx.com/api/v5/public/time")) Log(info) // 一个带参数的GET请求示例 var ticker = JSON.parse(HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT")) Log(ticker) }pythonimport json import urllib.request def main(): # HttpQuery不支持Python,可以使用urllib/urllib2库替代 info = json.loads(urllib.request.urlopen("https://www.okx.com/api/v5/public/time").read().decode('utf-8')) Log(info) ticker = json.loads(urllib.request.urlopen("https://www.okx.com/api/v5/market/books?instId=BTC-USDT").read().decode('utf-8')) Log(ticker)c++void main() { auto info = json::parse(HttpQuery("https://www.okx.com/api/v5/public/time")); Log(info); auto ticker = json::parse(HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT")); Log(ticker); } -
Example of using proxy settings with HttpQuery function.
javascriptfunction main() { // 本次设置代理并发送HTTP请求,无用户名、无密码,此次HTTP请求将通过代理发送 HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/") // 本次设置代理并发送HTTP请求,包含用户名和密码,仅对当前HttpQuery调用生效,后续调用HttpQuery("http://www.baidu.com")将不会使用代理 HttpQuery("socks5://username:[email protected]:8889/http://www.baidu.com/") }python# HttpQuery不支持Python,可以使用Python的urllib2库c++void main() { HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/"); HttpQuery("socks5://username:[email protected]:8889/http://www.baidu.com/"); }
Returns
| Type | Description |
string / object | Returns the response data of the request. If the return value is a |
Arguments
| Name | Type | Required | Description |
url | string | Yes | URL address for the HTTP request. |
options | object | No | HTTP request configuration parameters, can use the following structure:
All fields in this structure are optional, for example, the |
See Also
Remarks
The HttpQuery() function only supports JavaScript and C++ languages. Python language can use the urllib library to send HTTP requests directly. HttpQuery() is mainly used to access exchange interfaces that do not require signatures, such as public interfaces like market data.
In the backtesting system, HttpQuery() can be used to send requests (only GET requests are supported) to obtain data. During backtesting, access to different URLs is limited to a maximum of 20 times, and HttpQuery() access will cache data. When the same URL is accessed for the second time, the HttpQuery() function returns cached data without initiating an actual network request.
HttpQuery_Go
Send HTTP request, asynchronous version of the HttpQuery function.
HttpQuery_Go(url)
HttpQuery_Go(url, options)Examples
Asynchronously access exchange public interface to get aggregated market data.
javascript
function main() {
// 创建第一个异步线程
var r1 = HttpQuery_Go("https://www.okx.com/api/v5/market/tickers?instType=SPOT")
// 创建第二个异步线程
var r2 = HttpQuery_Go("https://api.huobi.pro/market/tickers")
// 获取第一个异步线程调用的返回值
var tickers1 = r1.wait()
// 获取第二个异步线程调用的返回值
var tickers2 = r2.wait()
// 打印结果
Log("tickers1:", tickers1)
Log("tickers2:", tickers2)
}
python
# 不支持
c++
// 不支持Returns
| Type | Description |
object | The |
Arguments
| Name | Type | Required | Description |
url | string | Yes | URL address for the HTTP request. |
options | object | No | HTTP request configuration parameters, can use the following structure:
All fields in this structure are optional, for example, you don't need to set the |
See Also
Remarks
The HttpQuery_Go() function only supports JavaScript language, Python language can use the urllib library to send HTTP requests directly. HttpQuery_Go() is mainly used to access exchange interfaces that do not require signatures, such as public interfaces like market data. The backtesting system does not support the HttpQuery_Go function.
Encode
This function encodes data based on the parameters passed in.
Encode(algo, inputFormat, outputFormat, data)
Encode(algo, inputFormat, outputFormat, data, keyFormat, key)Examples
-
Encode function call example.
javascriptfunction main() { Log(Encode("raw", "raw", "hex", "example", "raw", "123")) // 6578616d706c65 Log(Encode("raw", "raw", "hex", "example")) // 6578616d706c65 Log(Encode("sha256", "raw", "hex", "example", "raw", "123")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("sha256", "raw", "hex", "example", "", "123")) // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c Log(Encode("sha256", "raw", "hex", "example", null, "123")) // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c Log(Encode("sha256", "raw", "hex", "example", "string", "123")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("raw", "raw", "hex", "123")) // 313233 Log(Encode("raw", "raw", "base64", "123")) // MTIz Log(Encode("sha256", "raw", "hex", "example", "hex", "313233")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba }pythondef main(): Log(Encode("raw", "raw", "hex", "example", "raw", "123")) # 6578616d706c65 Log(Encode("raw", "raw", "hex", "example", "", "")) # 6578616d706c65 Log(Encode("sha256", "raw", "hex", "example", "raw", "123")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("sha256", "raw", "hex", "example", "", "123")) # 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c Log(Encode("sha256", "raw", "hex", "example", "string", "123")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("raw", "raw", "hex", "123", "", "")) # 313233 Log(Encode("raw", "raw", "base64", "123", "", "")) # MTIz Log(Encode("sha256", "raw", "hex", "example", "hex", "313233")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84bac++void main() { Log(Encode("raw", "raw", "hex", "example", "raw", "123")); // 6578616d706c65 Log(Encode("raw", "raw", "hex", "example")); // 6578616d706c65 Log(Encode("sha256", "raw", "hex", "example", "raw", "123")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("sha256", "raw", "hex", "example", "", "123")); // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c Log(Encode("sha256", "raw", "hex", "example", "string", "123")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("raw", "raw", "hex", "123")); // 313233 Log(Encode("raw", "raw", "base64", "123")); // MTIz Log(Encode("sha256", "raw", "hex", "example", "hex", "313233")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba } -
The parameter
algoalso supports the following values: "text.encoder.utf8", "text.decoder.utf8", "text.encoder.gbk", "text.decoder.gbk", for encoding and decoding strings.javascriptfunction main(){ var ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好") // e4bda0e5a5bd Log(ret1) var ret2 = Encode("text.decoder.utf8", "hex", "string", ret1) Log(ret2) var ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好") // c4e3bac3 Log(ret3) var ret4 = Encode("text.decoder.gbk", "hex", "string", ret3) Log(ret4) }pythondef main(): ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好", "", "") # e4bda0e5a5bd Log(ret1) ret2 = Encode("text.decoder.utf8", "hex", "string", ret1, "", "") Log(ret2) ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好", "", "") # c4e3bac3 Log(ret3) ret4 = Encode("text.decoder.gbk", "hex", "string", ret3, "", "") Log(ret4)c++void main(){ auto ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好"); // e4bda0e5a5bd Log(ret1); auto ret2 = Encode("text.decoder.utf8", "hex", "string", ret1); Log(ret2); auto ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好"); // c4e3bac3 Log(ret3); auto ret4 = Encode("text.decoder.gbk", "hex", "string", ret3); Log(ret4); }
Returns
| Type | Description |
string | The |
Arguments
| Name | Type | Required | Description |
algo | string | Yes | The parameter The parameter The parameter |
inputFormat | string | Yes | Specifies the data format of the |
outputFormat | string | Yes | Specifies the output data format. The |
data | string | Yes | The parameter |
keyFormat | string | No | Specifies the data format of the |
key | string | No | The parameter When the parameter When the parameter |
Remarks
The Encode() function only supports live trading. If the key and keyFormat parameters are not passed, key encryption is not used.
UnixNano
Get the current Unix timestamp in nanoseconds.
UnixNano()Examples
To get a millisecond timestamp, use the following code:
javascript
function main() {
var time = UnixNano() / 1000000
Log(_N(time, 0))
}
python
def main():
time = UnixNano()
Log(time)
c++
void main() {
auto time = UnixNano();
Log(time);
}Returns
| Type | Description |
number | The |
See Also
Unix
Get the current timestamp in seconds.
Unix()Examples
javascript
function main() {
var t = Unix()
Log(t)
}
python
def main():
t = Unix()
Log(t)
c++
void main() {
auto t = Unix();
Log(t);
}Returns
| Type | Description |
number | Returns timestamp in seconds. |
See Also
GetOS
Get the operating system information of the device where the docker is located.
GetOS()Examples
javascript
function main() {
Log("GetOS:", GetOS())
}
python
def main():
Log("GetOS:", GetOS())
c++
void main() {
Log("GetOS:", GetOS());
}Returns
| Type | Description |
string | Operating system information. |
Remarks
For example, when calling GetOS() function on a docker running on Mac OS operating system, it may return: darwin/amd64, because Apple computers support multiple hardware architectures. Where darwin is the kernel name of the Mac OS system.
MD5
Calculate the MD5 hash value of the parameter data.
MD5(data)Examples
javascript
function main() {
Log("MD5", MD5("hello world"))
}
python
def main():
Log("MD5", MD5("hello world"))
c++
void main() {
Log("MD5", MD5("hello world"));
}Returns
| Type | Description |
string | MD5 hash value. |
Arguments
| Name | Type | Required | Description |
data | string | Yes | The data to be MD5 calculated. |
See Also
Remarks
Calling the MD5("hello world") function returns: 5eb63bbbe01eeed093cb22bb8f5acdc3.
DBExec
Database interface function.
DBExec(sql)Examples
-
Supports in-memory database. For the
DBExecfunction parameters, if the SQL statement starts with:, it operates in the in-memory database without writing to files, which is faster. Suitable for database operations that don't require persistent storage, for example:javascriptfunction main() { var strSql = [ ":CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ].join("") var ret = DBExec(strSql) Log(ret) // 增加一条数据 Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) // 查询数据 Log(DBExec(":SELECT * FROM TEST_TABLE;")) }pythondef main(): arr = [ ":CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ] strSql = "" for i in range(len(arr)): strSql += arr[i] ret = DBExec(strSql) Log(ret) # 增加一条数据 Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) # 查询数据 Log(DBExec(":SELECT * FROM TEST_TABLE;"))c++void main() { string strSql = ":CREATE TABLE TEST_TABLE(\n TS INT PRIMARY KEY NOT NULL,\n HIGH REAL NOT NULL,\n OPEN REAL NOT NULL,\n LOW REAL NOT NULL,\n CLOSE REAL NOT NULL,\n VOLUME REAL NOT NULL)"; auto ret = DBExec(strSql); Log(ret); // 增加一条数据 Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")); // 查询数据 Log(DBExec(":SELECT * FROM TEST_TABLE;")); } -
Use the
DBExec()function to create a table.javascriptfunction main() { var strSql = [ "CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ].join("") var ret = DBExec(strSql) Log(ret) }pythondef main(): arr = [ "CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ] strSql = "" for i in range(len(arr)): strSql += arr[i] ret = DBExec(strSql) Log(ret)c++void main() { string strSql = "CREATE TABLE TEST_TABLE(\ TS INT PRIMARY KEY NOT NULL,\ HIGH REAL NOT NULL,\ OPEN REAL NOT NULL,\ LOW REAL NOT NULL,\ CLOSE REAL NOT NULL,\ VOLUME REAL NOT NULL)"; auto ret = DBExec(strSql); Log(ret); } -
CRUD operations on table records.
javascriptfunction main() { var strSql = [ "CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ].join("") Log(DBExec(strSql)) // 增加一条数据 Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) // 查询数据 Log(DBExec("SELECT * FROM TEST_TABLE;")) // 修改数据 Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000)) // 删除数据 Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110)) }pythondef main(): arr = [ "CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ] strSql = "" for i in range(len(arr)): strSql += arr[i] Log(DBExec(strSql)) # 增加一条数据 Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) # 查询数据 Log(DBExec("SELECT * FROM TEST_TABLE;")) # 修改数据 Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000)) # 删除数据 Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))c++void main() { string strSql = "CREATE TABLE TEST_TABLE(\ TS INT PRIMARY KEY NOT NULL,\ HIGH REAL NOT NULL,\ OPEN REAL NOT NULL,\ LOW REAL NOT NULL,\ CLOSE REAL NOT NULL,\ VOLUME REAL NOT NULL)"; Log(DBExec(strSql)); // 增加一条数据 Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")); // 查询数据 Log(DBExec("SELECT * FROM TEST_TABLE;")); // 修改数据 Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000)); // 删除数据 Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110)); }
Returns
| Type | Description |
object | Object containing SQL statement execution results, for example:
|
Arguments
| Name | Type | Required | Description |
sql | string | Yes | SQL statement string. |
See Also
Remarks
-
The function
DBExec()can operate on the live trading database (SQLite database) through passed parameters. -
Implements operations such as insert, delete, query, and update on data in the live trading database, supporting SQLite syntax.
-
System reserved tables in the live trading database:
kvdb,cfg,log,profit,chart. Please do not operate on these tables. -
Currently does not support transactions. Such operations are not recommended as they may cause system conflicts.
-
The
DBExec()function only supports live trading environment.
UUID
Create a UUID.
UUID()Examples
javascript
function main() {
var uuid1 = UUID()
var uuid2 = UUID()
Log(uuid1, uuid2)
}
python
def main():
uuid1 = UUID()
uuid2 = UUID()
Log(uuid1, uuid2)
c++
void main() {
auto uuid1 = UUID();
auto uuid2 = UUID();
Log(uuid1, uuid2);
}Returns
| Type | Description |
string | Returns a 32-character UUID string. |
Remarks
The UUID() function is only supported in live trading environment.
EventLoop
Listens for events and returns when any WebSocket has readable data or when concurrent tasks such as exchange.Go(), HttpQuery_Go(), etc. are completed.
EventLoop()
EventLoop(timeout)Examples
javascript
function main() {
var routine_getTicker = exchange.Go("GetTicker")
var routine_getDepth = exchange.Go("GetDepth")
var routine_getTrades = exchange.Go("GetTrades")
// Sleep(2000), if you use the Sleep statement here, it will cause the subsequent EventLoop function to miss previous events, because after waiting for 2 seconds, the concurrent functions have already received data, and the EventLoop listening mechanism starts afterwards, thus missing these events
// Unless you call EventLoop(-1) at the very first line of code to initialize the EventLoop listening mechanism first, then these events will not be missed
// Log("GetDepth:", routine_getDepth.wait()) If you call the wait function here in advance to retrieve the result of the GetDepth concurrent call, the event of GetDepth function receiving the request result will not be returned in the EventLoop function
var ts1 = new Date().getTime()
var ret1 = EventLoop(0)
var ts2 = new Date().getTime()
var ret2 = EventLoop(0)
var ts3 = new Date().getTime()
var ret3 = EventLoop(0)
Log("First concurrent task completed:", _D(ts1), ret1)
Log("Second concurrent task completed:", _D(ts2), ret2)
Log("Third concurrent task completed:", _D(ts3), ret3)
Log("GetTicker:", routine_getTicker.wait())
Log("GetDepth:", routine_getDepth.wait())
Log("GetTrades:", routine_getTrades.wait())
}
python
import time
def main():
routine_getTicker = exchange.Go("GetTicker")
routine_getDepth = exchange.Go("GetDepth")
routine_getTrades = exchange.Go("GetTrades")
ts1 = time.time()
ret1 = EventLoop(0)
ts2 = time.time()
ret2 = EventLoop(0)
ts3 = time.time()
ret3 = EventLoop(0)
Log("First concurrent task completed:", _D(ts1), ret1)
Log("Second concurrent task completed:", _D(ts2), ret2)
Log("Third concurrent task completed:", _D(ts3), ret3)
Log("GetTicker:", routine_getTicker.wait())
Log("GetDepth:", routine_getDepth.wait())
Log("GetTrades:", routine_getTrades.wait())
c++
void main() {
auto routine_getTicker = exchange.Go("GetTicker");
auto routine_getDepth = exchange.Go("GetDepth");
auto routine_getTrades = exchange.Go("GetTrades");
auto ts1 = Unix() * 1000;
auto ret1 = EventLoop(0);
auto ts2 = Unix() * 1000;
auto ret2 = EventLoop(0);
auto ts3 = Unix() * 1000;
auto ret3 = EventLoop(0);
Log("First concurrent task completed:", _D(ts1), ret1);
Log("Second concurrent task completed:", _D(ts2), ret2);
Log("Third concurrent task completed:", _D(ts3), ret3);
Ticker ticker;
Depth depth;
Trades trades;
routine_getTicker.wait(ticker);
routine_getDepth.wait(depth);
routine_getTrades.wait(trades);
Log("GetTicker:", ticker);
Log("GetDepth:", depth);
Log("GetTrades:", trades);
}Returns
| Type | Description |
object | If the returned object is not null, the
|
Arguments
| Name | Type | Required | Description |
timeout | number | No | The parameter |
See Also
Remarks
The event listening mechanism is initialized only when the EventLoop() function is called for the first time in the code. If EventLoop() is called for the first time after event callbacks have occurred, previous events will be missed. The underlying system's encapsulated queue structure caches up to 500 event callbacks. If the EventLoop() function is not called in time during program execution to retrieve events, later event callbacks exceeding the 500 cache limit will be lost.
Calling the EventLoop() function does not affect the underlying system's WebSocket cache queue, nor does it affect the cache of concurrent functions such as exchange.Go(). For these caches, you still need to use their respective methods to retrieve data. Data that has been retrieved before the EventLoop() function returns will not generate return events in the EventLoop() function.
The main purpose of the EventLoop() function is to notify the strategy layer that the underlying system has received new network data, driving the entire strategy through events. When the EventLoop() function returns an event, simply iterate through all data sources, such as WebSocket connections and objects created by exchange.Go(), to attempt to retrieve data.
The EventLoop() function only supports live trading environments.
When called in the main function main(), it listens for events in the main thread. In strategies written in JavaScript, threads created by the threading.Thread() function can call this function within their execution functions to listen for events in the current thread.
__Serve
The __Serve function is used to create HTTP services, TCP services, and WebSocket services (based on HTTP protocol).
__Serve(serveURI, handler)
__Serve(serveURI, handler, ...args)Examples
javascript
function main() {
let httpServer = __Serve("http://:8088?gzip=true", function (ctx) {
Log("http connect from: ", ctx.remoteAddr(), "->", ctx.localAddr())
let path = ctx.path()
if (path == "/") {
ctx.write(JSON.stringify({
path: ctx.path(),
method: ctx.method(),
headers: ctx.headers(),
cookie: ctx.header("Cookie"),
remote: ctx.remoteAddr(),
query: ctx.rawQuery()
}))
} else if (path == "/tickers") {
let ret = exchange.GetTickers()
if (!ret) {
ctx.setStatus(500)
ctx.write(GetLastError())
} else {
ctx.write(JSON.stringify(ret))
}
} else if (path == "/wss") {
if (ctx.upgrade("websocket")) { // upgrade to websocket
while (true) {
let r = ctx.read(10)
if (r == "") {
break
} else if (r) {
if (r == "ticker") {
ctx.write(JSON.stringify(exchange.GetTicker()))
} else {
ctx.write("not support")
}
}
}
Log("websocket closed", ctx.remoteAddr())
}
} else {
ctx.setStatus(404)
}
})
let echoServer = __Serve("tcp://:8089", function (ctx) {
Log("tcp connect from: ", ctx.remoteAddr(), "->", ctx.localAddr())
while (true) {
let d = ctx.read()
if (!d) {
break
}
ctx.write(d)
}
Log("connect closed")
})
Log("http serve on", httpServer, "tcp serve on", echoServer)
for (var i = 0; i < 5; i++) {
if (i == 2) {
// test Http
var retHttp = HttpQuery("http://127.0.0.1:8088?num=123&limit=100", {"debug": true})
Log("retHttp:", retHttp)
} else if (i == 3) {
// test TCP
var tcpConn = Dial("tcp://127.0.0.1:8089")
tcpConn.write("Hello TCP Server")
var retTCP = tcpConn.read()
Log("retTCP:", retTCP)
} else if (i == 4) {
// test Websocket
var wsConn = Dial("ws://127.0.0.1:8088/wss|compress=gzip")
wsConn.write("ticker")
var retWS = wsConn.read(1000)
Log("retWS:", retWS)
// no depth
wsConn.write("depth")
retWS = wsConn.read(1000)
Log("retWS:", retWS)
}
Sleep(1000)
}
}
python
# Not supported
c++
// Not supportedReturns
| Type | Description |
string | Returns a string recording the IP address and port of the created service. For example: |
Arguments
| Name | Type | Required | Description |
serveURI | string | Yes | The
|
handler | function | Yes | The The callback function passed in via the |
arg | string / number / bool / object / array / function / any (any type supported by the platform) | No | As the actual arguments for the parameters of the callback function passed in via the
The parameters |
See Also
Remarks
-
This function only supports JavaScript language strategies.
-
The service thread is isolated from the global scope, so it does not support closures or references to external variables, custom functions, etc.; however, all platform API functions can be called.
-
WebSocketservice is implemented based on HTTP protocol. You can set a routing branch in the path and design the implementation code forWebSocketmessage subscription/push. Please refer to the example code in this section.
-
The callback handler function passed in the handler parameter receives a ctx parameter. The ctx parameter is a context object used to get data and write data, with the following methods:
- ctx.proto()
Applies to HTTP/TCP protocol, returns the protocol name when called. For example:HTTP/1.1,tcp. - ctx.host()
Applies to HTTP protocol, returns host information when called: IP address, port. - ctx.path()
Applies to HTTP protocol, returns the request path when called. - ctx.query(key)
Applies to HTTP protocol, returns the value corresponding to the key in the query of the request when called. For example, if the request sent is:http://127.0.0.1:8088?num=123, callingctx.query("num")in the callback handler function passed in thehandlerparameter returns"123". - ctx.rawQuery()
Applies to HTTP protocol, returns the raw query in the request (query of the HTTP request) when called. - ctx.headers()
Applies to HTTP protocol, returns the request header information in the request when called. - ctx.header(key)
Applies to HTTP protocol, returns the value corresponding to a specific key in the specified request header when called. For example, to get theUser-Agentin the headers of the current request:ctx.header("User-Agent"). - ctx.method()
Applies to HTTP protocol, returns the request method when called, such asGET,POST, etc. - ctx.body()
Applies to POST requests of HTTP protocol, returns the body of the request when called. - ctx.setHeader(key, value)
Applies to HTTP protocol, sets the request header information of the response message. - ctx.setStatus(code)
Applies to HTTP protocol, sets the HTTP message status code. Usually the HTTP status code is set at the end of the routing branch, default is 200. - ctx.remoteAddr()
Applies to HTTP/TCP protocol, returns the remote client address and port in the request when called. - ctx.localAddr()
Applies to HTTP/TCP protocol, returns the local service address and port when called. - ctx.upgrade("websocket")
Applies to WebSocket protocol implementation based on HTTP protocol, switches thectxcontext object to WebSocket protocol; returns boolean value (true) on successful switch, boolean value (false) on failure. - ctx.read(timeout_ms)
Applies to WebSocket protocol implementation based on HTTP protocol/TCP protocol, reads data from WebSocket connection or TCP connection. Thereadmethod is not supported in regular HTTP protocol; you can specify the timeout parametertimeout_msin milliseconds. - ctx.write(s)
Applies to HTTP/TCP protocol, used to write string data. You can useJSON.stringify()to encode JSON objects as strings before writing. ForWebSocketprotocol, this method can be used to pass the encoded string to the client.
_G
Persistently save data. This function implements a savable global dictionary feature. The data structure is a key-value pair table, permanently saved in the docker's local database file.
_G()
_G(k)
_G(k, v)Examples
javascript
function main(){
// 设置一个全局变量num,值为1
_G("num", 1)
// 更改一个全局变量num,值为字符串ok
_G("num", "ok")
// 删除全局变量num
_G("num", null)
// 返回全局变量num的值
Log(_G("num"))
// 删除所有全局变量
_G(null)
// 返回实盘ID
var robotId = _G()
}
python
def main():
_G("num", 1)
_G("num", "ok")
_G("num", None)
Log(_G("num"))
_G(None)
robotId = _G()
c++
void main() {
_G("num", 1);
_G("num", "ok");
_G("num", NULL);
Log(_G("num"));
_G(NULL);
// 不支持 auto robotId = _G();
}Returns
| Type | Description |
string / number / bool / object / array / null | The key value data in the persistently saved |
Arguments
| Name | Type | Required | Description |
k | string / null | No | The parameter |
v | string / number / bool / object / array / null | No | The parameter |
See Also
Remarks
Each live trading uses a separate database. When the strategy restarts or the docker stops running, the data saved by the _G() function will persist. If the backtest ends, the data saved by the _G() function in the backtest system will be cleared. When using the _G() function to persistently save data, it should be used reasonably according to the memory and disk space of the hardware device to avoid abuse.
In live trading, when calling the _G() function without passing any parameters, the _G() function returns the Id of the current live trading.
When calling the _G() function, passing null for parameter v means deleting the k-v key-value pair.
When calling the _G() function and only passing parameter k (string type), the _G() function returns the saved key value corresponding to parameter k.
When calling the _G() function and only passing null for parameter k, it means deleting all recorded k-v key-value pairs.
After a k-v key-value pair has been persistently saved, calling the _G() function again with the already persistently saved key name as parameter k and a new key value as parameter v will update the k-v key-value pair.
Taking live trading Id 123456 as an example, the key-value pair data persistently saved using the _G() function is stored in the database file /logs/storage/123456/123456.db3 under the docker directory to which the live trading (i.e., strategy instance program) belongs, and the data is recorded in the kvdb table.
_D
Convert millisecond timestamp or Date object to time string.
_D()
_D(timestamp)
_D(timestamp, fmt)Examples
-
Get and print current time string:
javascriptfunction main(){ var time = _D() Log(time) }pythondef main(): strTime = _D() Log(strTime)c++void main() { auto strTime = _D(); Log(strTime); } -
Timestamp is 1574993606000, convert using code:
javascriptfunction main() { Log(_D(1574993606000)) }pythondef main(): # Running on Beijing time server: 2019-11-29 10:13:26, running this code on a docker on another regional server results in: 2019-11-29 02:13:26 Log(_D(1574993606))c++void main() { Log(_D(1574993606000)); } -
Use parameter
fmtfor formatting. Format strings differ betweenJavaScript,Python, andC++languages. See the following examples for details:javascriptfunction main() { Log(_D(1574993606000, "yyyy--MM--dd hh--mm--ss")) // 2019--11--29 10--13--26 }pythondef main(): # 1574993606 is a second-level timestamp Log(_D(1574993606, "%Y--%m--%d %H--%M--%S")) # 2019--11--29 10--13--26c++void main() { Log(_D(1574993606000, "%Y--%m--%d %H--%M--%S")); // 2019--11--29 10--13--26 }
Returns
| Type | Description |
string | Time string. |
Arguments
| Name | Type | Required | Description |
timestamp | number / object | No | Millisecond timestamp or |
fmt | string | No | Format string. |
See Also
Remarks
Returns current time string when no parameters are passed. When using _D() function in Python strategies, note that the parameter passed should be a second-level timestamp (JavaScript and C++ strategies use millisecond-level timestamps, 1 second equals 1000 milliseconds). When using _D() function in live trading environment to parse timestamps into readable time strings, pay attention to the timezone and time settings of the operating system where the docker is located. The _D() function parses timestamps into readable time strings based on the local time of the docker system.
_N
Format floating-point numbers.
_N()
_N(num)
_N(num, precision)Examples
-
For example,
_N(3.1415, 2)will remove digits beyond two decimal places from3.1415, and the function returns3.14.javascriptfunction main(){ var i = 3.1415 Log(i) var ii = _N(i, 2) Log(ii) }pythondef main(): i = 3.1415 Log(i) ii = _N(i, 2) Log(ii)c++void main() { auto i = 3.1415; Log(i); auto ii = _N(i, 2); Log(ii); } -
To set N digits to the left of the decimal point to 0, you can use negative precision:
javascriptfunction main(){ var i = 1300 Log(i) var ii = _N(i, -3) // 查看日志得知为1000 Log(ii) }pythondef main(): i = 1300 Log(i) ii = _N(i, -3) Log(ii)c++void main() { auto i = 1300; Log(i); auto ii = _N(i, -3); Log(ii); }
Returns
| Type | Description |
number | The formatted floating-point number according to the precision setting. |
Arguments
| Name | Type | Required | Description |
num | number | Yes | The floating-point number to be formatted. |
precision | number | No | The precision setting for formatting. The parameter |
See Also
Remarks
The parameter precision can be a positive or negative integer.
_C
Retry function for interface fault tolerance handling.
_C(pfn)
_C(pfn, ...args)Examples
-
For fault tolerance of functions without parameters:
javascriptfunction main(){ var ticker = _C(exchange.GetTicker) // Adjust the retry interval of _C() function to 2 seconds _CDelay(2000) var depth = _C(exchange.GetDepth) Log(ticker) Log(depth) }pythondef main(): ticker = _C(exchange.GetTicker) _CDelay(2000) depth = _C(exchange.GetDepth) Log(ticker) Log(depth)c++void main() { auto ticker = _C(exchange.GetTicker); _CDelay(2000); auto depth = _C(exchange.GetDepth); Log(ticker); Log(depth); } -
For fault tolerance of functions with parameters:
javascriptfunction main(){ var records = _C(exchange.GetRecords, PERIOD_D1) Log(records) }pythondef main(): records = _C(exchange.GetRecords, PERIOD_D1) Log(records)c++void main() { auto records = _C(exchange.GetRecords, PERIOD_D1); Log(records); } -
Can also be used for fault tolerance handling of custom functions:
javascriptvar test = function(a, b){ var time = new Date().getTime() / 1000 if(time % b == 3){ Log("Condition met!", "#FF0000") return true } Log("Retrying!", "#FF0000") return false } function main(){ var ret = _C(test, 1, 5) Log(ret) }pythonimport time def test(a, b): ts = time.time() if ts % b == 3: Log("Condition met!", "#FF0000") return True Log("Retrying!", "#FF0000") return False def main(): ret = _C(test, 1, 5) Log(ret)c++// C++ does not support this method for custom function fault tolerance
Returns
| Type | Description |
All platform-supported types except false values and null values (any). | The return value when the callback function is executed. |
Arguments
| Name | Type | Required | Description |
pfn | function | Yes | The parameter |
arg | string / number / bool / object / array / function / any (any type supported by the platform) | No | Parameters for the callback function. There may be multiple |
Remarks
The _C() function will continuously call the specified function until it returns successfully (it will retry calling pfn when the function referenced by parameter pfn returns null or false).
For example _C(exchange.GetTicker). The default retry interval is 3 seconds. You can call the _CDelay() function to set the retry interval.
For example _CDelay(1000) means changing the retry interval of the _C() function to 1 second.
The following functions can be (but are not limited to) handled for fault tolerance:
exchange.GetTicker()exchange.GetDepth()exchange.GetTrades()exchange.GetRecords()exchange.GetAccount()exchange.GetOrders()exchange.GetOrder()exchange.GetPositions()
All the above functions can be called through the _C() function to achieve fault tolerance. The _C() function is not limited to fault tolerance for the above functions. The parameter pfn is a function reference, not a function call. Note that you should use _C(exchange.GetTicker) instead of _C(exchange.GetTicker()).
_Cross
Returns the number of periods since array arr1 crossed array arr2.
_Cross(arr1, arr2)Examples
You can simulate a set of data to test the _Cross(Arr1, Arr2) function:
javascript
// 快线指标
var arr1 = [1,2,3,4,5,6,8,8,9]
// 慢线指标
var arr2 = [2,3,4,5,6,7,7,7,7]
function main(){
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
}
python
arr1 = [1,2,3,4,5,6,8,8,9]
arr2 = [2,3,4,5,6,7,7,7,7]
def main():
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
c++
void main() {
vector<double> arr1 = {1,2,3,4,5,6,8,8,9};
vector<double> arr2 = {2,3,4,5,6,7,7,7,7};
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2));
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1));
}Returns
| Type | Description |
number | The number of periods since array |
Arguments
| Name | Type | Required | Description |
arr1 | array | Yes | Array with elements of type |
arr2 | array | Yes | Array with elements of type |
Remarks
When the return value of _Cross() function is positive, it indicates the number of periods since upward crossover; when negative, it indicates the number of periods since downward crossover; when 0, it indicates the current values are equal. For detailed usage instructions: Built-in Function _Cross Analysis and Usage Guide.
JSON.parse
The JSON.parse function is a method of the ECMAScript standard built-in object JSON, used for parsing JSON strings. The FMZ Quant Trading Platform has extended it by adding a safeStr parameter.
JSON.parse(s)
JSON.parse(s, safeStr)Examples
Parse JSON strings containing large numeric values.
javascript
function main() {
let s1 = '{"num": 8754613216564987646512354656874651651358}'
Log("JSON.parse:", JSON.parse(s1)) // JSON.parse: {"num":8.754613216564987e+39}
Log("JSON.parse:", JSON.parse(s1, true)) // JSON.parse: {"num":"8754613216564987646512354656874651651358"}
let s2 = '{"num": 123}'
Log("JSON.parse:", JSON.parse(s2)) // JSON.parse: {"num":123}
Log("JSON.parse:", JSON.parse(s2, true)) // JSON.parse: {"num":123}
}
python
# 可以使用Python的第三方库处理大数值数据。
c++
// 可以使用其它方案处理。Returns
| Type | Description |
object | Returns the parsed |
Arguments
| Name | Type | Required | Description |
s | string | Yes | The |
safeStr | bool | No | When this parameter is set to |
Remarks
The JSON.parse() function can correctly parse JSON strings containing large numeric values. When the safeStr parameter is set to a truthy value, large numeric values will be parsed as string types.
The safeStr parameter also supports being used as a reviver parameter, i.e., a function that transforms the result. This function will be called for each member of the object. Please refer to relevant documentation for details.
Only supported in JavaScript language.
The safeStr parameter feature of the JSON.parse() function is not supported in the backtesting system.
JSON.stringify
The JSON.stringify function is a method of the ECMAScript standard built-in object JSON, used to convert JavaScript values to JSON strings.
JSON.stringify(obj)Examples
Serialize an object to a JSON string and output it.
javascript
function main() {
let s1 = {"num": "8754613216564987646512354656874651651358"}
Log("JSON.stringify:", JSON.stringify(s1))
// JSON.stringify: {"num":"8754613216564987646512354656874651651358"}
// The variable returned by JSON.stringify(s1) is of string type
}
python
// Omitted
c++
// OmittedReturns
| Type | Description |
string | Returns the serialized |
Arguments
| Name | Type | Required | Description |
obj | string / number / bool / object / array / function / any (any type supported by the platform) | Yes | The value to be serialized into a JSON string. |
Remarks
Only supported in JavaScript language.
SetChannelData
Publishes the latest status data to a channel. This function is used for inter-live trading communication, allowing the current live trading instance to broadcast its status data to a channel for other live trading instances to subscribe and retrieve.
SetChannelData(data)Examples
-
Channel Broadcaster Example - Publishing BTC Price Market Data
javascriptfunction main() { var updateId = 0 var robotId = _G() // Get current live trading bot ID while(true) { // Get actual market price var ticker = exchange.GetTicker("BTC_USDT") if (!ticker) { Sleep(5000) continue } // Prepare current channel state data var channelState = { robotId: robotId, updateId: ++updateId, timestamp: Date.now(), symbol: "BTC_USDT", lastPrice: ticker.Last, volume: ticker.Volume, high: ticker.High, low: ticker.Low } // Publish latest state on channel (overwrites old state) SetChannelData(channelState) // Display current channel state LogStatus("Channel Broadcaster [Bot ID: " + robotId + "]\n" + "Update ID: #" + channelState.updateId + "\n" + "Time: " + _D(channelState.timestamp) + "\n" + "Symbol: " + channelState.symbol + "\n" + "Last Price: $" + channelState.lastPrice.toFixed(2) + "\n" + "Volume: " + channelState.volume.toFixed(4) + "\n" + "High: $" + channelState.high.toFixed(2) + "\n" + "Low: $" + channelState.low.toFixed(2)) Sleep(60000) // Update channel state every minute } }pythondef main(): updateId = 0 robotId = _G() # Get current live trading bot ID while True: # Get actual market price ticker = exchange.GetTicker("BTC_USDT") if not ticker: Sleep(5000) continue # Prepare current channel state data channelState = { "robotId": robotId, "updateId": updateId + 1, "timestamp": time.time() * 1000, "symbol": "BTC_USDT", "lastPrice": ticker["Last"], "volume": ticker["Volume"], "high": ticker["High"], "low": ticker["Low"] } updateId += 1 # Publish latest state on channel (overwrites old state) SetChannelData(channelState) # Display current channel state LogStatus("Channel Broadcaster [Bot ID: {}]\n".format(robotId) + "Update ID: #{}\n".format(channelState["updateId"]) + "Time: {}\n".format(_D(channelState["timestamp"])) + "Symbol: {}\n".format(channelState["symbol"]) + "Last Price: ${:.2f}\n".format(channelState["lastPrice"]) + "Volume: {:.4f}\n".format(channelState["volume"]) + "High: ${:.2f}\n".format(channelState["high"]) + "Low: ${:.2f}".format(channelState["low"])) Sleep(60000) # Update channel state every minutec++ -
Cross-platform sending example - Simulating external platform (e.g., TradingView) sending data to FMZ live trading
javascript// This example demonstrates how to use HttpQuery to send HTTP POST requests, simulating external platforms sending data to FMZ live trading // In actual scenarios, external platforms (such as TradingView's Webhook alert URL, third-party trading systems, etc.) would directly call the FMZ API endpoint function main() { let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" let robotId = 123456 // Target live trading ID (the live trading instance that needs to receive data) let baseUrl = "https://www.fmz.com" while (true) { // Prepare the data to send (can be JSON, text, or other formats) let sendData = { "action": "buy", "symbol": "BTC_USDT", "price": 50000, "timestamp": Date.now() } // Construct HTTP POST request let options = { method: "POST", body: JSON.stringify(sendData) // body can be JSON string, plain text, etc. } let url = `${baseUrl}/api/v1?method=pub&robot=${robotId}&channel=${uuid}` // Send data let ret = HttpQuery(url, options) Log("Simulated external platform sending data, result:", ret) Sleep(10000) // Send every 10 seconds } }python# This example demonstrates how to use HttpQuery to send HTTP POST requests, simulating external platforms sending data to FMZ live trading # In actual scenarios, external platforms (such as TradingView's Webhook alert URL, third-party trading systems, etc.) would directly call the FMZ API endpoint import json def main(): uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" robotId = 123456 # Target live trading ID (the live trading instance that needs to receive data) baseUrl = "https://www.fmz.com" while True: # Prepare the data to send (can be JSON, text, or other formats) sendData = { "action": "buy", "symbol": "BTC_USDT", "price": 50000, "timestamp": time.time() * 1000 } # Construct HTTP POST request options = { "method": "POST", "body": json.dumps(sendData) # body can be JSON string, plain text, etc. } url = "{}/api/v1?method=pub&robot={}&channel={}".format(baseUrl, robotId, uuid) # Send data ret = HttpQuery(url, options) Log("Simulated external platform sending data, result:", ret) Sleep(10000) # Send every 10 secondsc++
Returns
| Type | Description |
null value | The function has no return value. |
Arguments
| Name | Type | Required | Description |
data | object / array / string / number / bool / null value | Yes | The data to be published to the channel, can be any data structure that can be |
See Also
Remarks
The SetChannelData() function is a non-blocking call that returns immediately after invocation without waiting for data transmission to complete.
Each live trading instance has an independent channel, and the channel ID is the live trading ID (which can be obtained through the _G() function).
Only the latest status data is saved on the channel. Each call to SetChannelData() overwrites the previously published data rather than appending historical messages.
Channel data can be broadcast across live trading instances, across dockers, and across servers. Multiple live trading instances can subscribe to the same channel.
The subscriber uses the GetChannelData() function to subscribe to channel data.
Channel communication is applicable to live trading environments; this feature may be limited in the backtesting system.
The byte length of the data parameter data after JSON serialization must not exceed 1024 bytes. Exceeding this limit may cause data publishing to fail. It is recommended to transmit only necessary status information and avoid transmitting overly large data objects.
Published data should be used reasonably according to the memory and network bandwidth of hardware devices. Avoid publishing overly large data objects.
In addition to being subscribed by other live trading instances within the FMZ platform, data published by the SetChannelData() function also supports cross-platform data sending functionality. External platforms (such as TradingView Webhook alerts, third-party trading systems, monitoring software, etc.) can send data to a specified FMZ live trading instance via HTTP POST requests.
Cross-platform data sending method: External systems send data to the FMZ platform API endpoint via HTTP POST request: https://www.fmz.com/api/v1?method=pub&robot={robotId}&channel={uuid}, where robotId is the target live trading ID, and uuid is a 32-character channel identifier. The data to be sent is passed in the request body and can be in JSON format, text, or other formats. Note: A live trading instance must first subscribe to the UUID channel before external systems can successfully send data; the broadcast data will be sent to all live trading instances under the docker where the robotId live trading instance is located, and all live trading instances under the same docker that have subscribed to the UUID channel can receive the data.
GetChannelData
Subscribe to channel data from a specified live trading instance. This function is used for inter-instance communication, allowing retrieval of the latest status data published by other live trading instances via the SetChannelData() function.
GetChannelData(channelId)Examples
-
Channel Subscriber Example - Subscribe to channel data from two live trading bots
javascriptfunction main() { // Two channel IDs to subscribe to (modify according to actual situation) var channelId1 = "632799" // Live trading bot ID for channel 1 var channelId2 = "632800" // Live trading bot ID for channel 2 while(true) { // Subscribe to current state of channel 1 var state1 = GetChannelData(channelId1) // Subscribe to current state of channel 2 var state2 = GetChannelData(channelId2) // Build status display var statusMsg = "Channel Subscriber - Current Subscription Status\n\n" // Display channel 1 status statusMsg += "═══ Channel 1 [" + channelId1 + "] ═══\n" if (state1 !== null) { statusMsg += "Update ID: #" + state1.updateId + "\n" statusMsg += "Time: " + _D(state1.timestamp) + "\n" statusMsg += "Symbol: " + state1.symbol + "\n" statusMsg += "Last Price: $" + state1.lastPrice.toFixed(2) + "\n" statusMsg += "Volume: " + state1.volume.toFixed(4) + "\n" } else { statusMsg += "Status: Waiting... (first call returns null)\n" } statusMsg += "\n" // Display channel 2 status statusMsg += "═══ Channel 2 [" + channelId2 + "] ═══\n" if (state2 !== null) { statusMsg += "Update ID: #" + state2.updateId + "\n" statusMsg += "Time: " + _D(state2.timestamp) + "\n" statusMsg += "Symbol: " + state2.symbol + "\n" statusMsg += "Last Price: $" + state2.lastPrice.toFixed(2) + "\n" statusMsg += "Volume: " + state2.volume.toFixed(4) + "\n" } else { statusMsg += "Status: Waiting... (first call returns null)\n" } LogStatus(statusMsg) Sleep(5000) // Subscribe to channels every 5 seconds } }pythondef main(): # Two channel IDs to subscribe to (modify according to actual situation) channelId1 = "632799" # Live trading bot ID for channel 1 channelId2 = "632800" # Live trading bot ID for channel 2 while True: # Subscribe to current state of channel 1 state1 = GetChannelData(channelId1) # Subscribe to current state of channel 2 state2 = GetChannelData(channelId2) # Build status display statusMsg = "Channel Subscriber - Current Subscription Status\n\n" # Display channel 1 status statusMsg += "═══ Channel 1 [{}] ═══\n".format(channelId1) if state1 is not None: statusMsg += "Update ID: #{}\n".format(state1["updateId"]) statusMsg += "Time: {}\n".format(_D(state1["timestamp"])) statusMsg += "Symbol: {}\n".format(state1["symbol"]) statusMsg += "Last Price: ${:.2f}\n".format(state1["lastPrice"]) statusMsg += "Volume: {:.4f}\n".format(state1["volume"]) else: statusMsg += "Status: Waiting... (first call returns None)\n" statusMsg += "\n" # Display channel 2 status statusMsg += "═══ Channel 2 [{}] ═══\n".format(channelId2) if state2 is not None: statusMsg += "Update ID: #{}\n".format(state2["updateId"]) statusMsg += "Time: {}\n".format(_D(state2["timestamp"])) statusMsg += "Symbol: {}\n".format(state2["symbol"]) statusMsg += "Last Price: ${:.2f}\n".format(state2["lastPrice"]) statusMsg += "Volume: {:.4f}\n".format(state2["volume"]) else: statusMsg += "Status: Waiting... (first call returns None)\n" LogStatus(statusMsg) Sleep(5000) # Subscribe to channels every 5 secondsc++ -
Cross-platform subscription example - Subscribe to data sent from external systems using UUID
javascriptfunction main() { // Use 32-bit UUID as channel identifier let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" while (true) { // Subscribe to data from UUID channel let data = GetChannelData(uuid) if (data !== null) { Log("Received cross-platform data:", data) } else { Log("Waiting for data... (first call returns null)") } Sleep(10000) // Check every 10 seconds } }pythondef main(): # Use 32-bit UUID as channel identifier uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" while True: # Subscribe to data from UUID channel data = GetChannelData(uuid) if data is not None: Log("Received cross-platform data:", data) else: Log("Waiting for data... (first call returns None)") Sleep(10000) # Check every 10 secondsc++
Returns
| Type | Description |
object / array / string / number / bool / null value | Returns the latest status data from the subscribed channel. The first call returns |
Arguments
| Name | Type | Required | Description |
channelId | string / number | Yes | Channel identifier, supports two types:
|
See Also
Remarks
The GetChannelData() function is a non-blocking call that returns immediately without waiting for data reception to complete.
The first call to the GetChannelData() function will return null, requiring retries to wait for channel data synchronization to complete.
Each call retrieves the latest status data on the channel, not a historical message queue.
A live trading instance can subscribe to channels from multiple different live trading instances simultaneously by calling GetChannelData() multiple times with different live trading IDs.
The current live trading instance can also subscribe to its own channel, meaning the robotId parameter can be the current live trading ID.
Channel data can be transmitted across live trading instances, across dockers, and across servers.
The broadcasting side uses the SetChannelData() function to publish channel data.
Channel communication is designed for live trading environments; this feature may be limited in the backtesting system.
The GetChannelData() function supports cross-platform subscription functionality. When using a 32-bit UUID as the channel identifier, it can receive data sent from external systems outside the FMZ platform via HTTP API. External systems need to specify the live trading ID and UUID to send data. All live trading instances under the same docker can subscribe to data from that UUID channel, while live trading instances under different dockers cannot subscribe.