Global
Version
Returns the current system version number.
Version()Examples
javascript
function main() {
Log("version:", Version())
}
python
def main():
Log("version:", Version())
rust
fn main() {
Log!("version:", Version());
}
c++
void main() {
Log("version:", Version());
}Returns
| Type | Description |
string | The current system version number, for example: |
Remarks
The system version number is the version number of the hosting program (docker/agent).
Sleep
The sleep function pauses program execution for a specified period of 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")
rust
fn main() {
Sleep(1000 * 10); // Wait for 10 seconds
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 the Sleep(1000) function, the program will sleep for 1 second. This function supports sleep operations of less than 1 millisecond, such as Sleep(0.1). The minimum supported parameter is 0.000001, i.e. nanosecond-level sleep, where 1 nanosecond equals 1e-6 milliseconds.
When writing strategies in Python, for operations such as polling intervals and time waiting, you should use the Sleep(millisecond) function rather than the time.sleep(second) function from Python's time library. This is because if a strategy uses the time.sleep(second) function during backtesting, it will cause the strategy program to actually wait for a period of time (instead of skipping ahead on the backtesting system's time series), resulting in very slow backtesting speed.
IsVirtual
Used to determine whether the strategy's runtime environment is the 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.")
rust
fn 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 | When the strategy runs in the backtesting system environment, it returns a truthy value, for example: |
Remarks
Used to determine whether the current runtime environment is the backtesting system, in order to accommodate the differences between the backtesting and live trading environments.
Send an 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")
rust
fn 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 a truthy value, such as |
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 | Used to specify the |
mailTo | string | Yes | Used to specify the email address of the email recipient. |
title | string | Yes | The email subject. |
body | string | Yes | The email body. |
See Also
Remarks
The smtpPassword parameter sets the password for the SMTP service, not the mailbox login password.
When setting the smtpServer parameter, if you need to change the port, you can append the port number directly in the smtpServer parameter. For example: the smtp.qq.com:587 port of QQ Mail has been tested and works.
If the error unencryped connection occurs, you need to modify the smtpServer parameter of the Mail function to the format ssl://xxx.com:xxx. For example, the ssl method for QQ Mail SMTP is ssl://smtp.qq.com:465, or use 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
Filters 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")rustfn 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 error messages from a specific interface.
javascriptfunction main() { // Query a non-existent order (id 123) to deliberately trigger an interface error var order = exchange.GetOrder("123") Log(order) // Filter http 502 errors and GetOrder interface errors; after setting the error filter, the second call to GetOrder will no longer 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)rustfn main() { // Query a non-existent order (id 123) to deliberately trigger an interface error let orderId = OrderId { S: "123".to_string(), ..Default::default() }; let order = exchange.GetOrder(&orderId); Log!(order); // Filter http 502 errors and GetOrder interface errors; after setting the error filter, the second call to GetOrder will no longer report an error SetErrorFilter("502:|GetOrder"); let order = exchange.GetOrder(&orderId); 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 | A regular expression string. |
Remarks
Error logs that match this regular expression will no longer be uploaded to the logging system. This function can be called multiple times (with no limit on the number of calls) to set multiple filter conditions; regular expressions set across multiple calls accumulate and take effect simultaneously. You can pass an empty string to reset the regular expression used to filter error logs: SetErrorFilter(""). Filtered logs will no longer be written to the database file corresponding to the live trading Id under the docker directory, thereby preventing the database file from bloating due to frequent errors.
GetPid
Get the ID of the live trading process.
GetPid()Examples
javascript
function main(){
var id = GetPid()
Log(id)
}
python
def main():
id = GetPid()
Log(id)
rust
fn main() {
let id = GetPid();
Log!(id);
}
c++
void main() {
auto id = GetPid();
Log(id);
}Returns
| Type | Description |
string | Returns the ID of the live trading process. |
GetLastError
Retrieves the most recent error message.
GetLastError()Examples
javascript
function main(){
// Since order number 123 does not exist, this will trigger an error
exchange.GetOrder("123")
var error = GetLastError()
Log(error)
}
python
def main():
exchange.GetOrder("123")
error = GetLastError()
Log(error)
rust
fn main() {
// Since order number 123 does not exist, this will trigger an error
// Rust's GetOrder accepts a &OrderId parameter; the string id must be placed in the S field
let id = OrderId { S: "123".to_string(), ..Default::default() };
let _ = exchange.GetOrder(&id);
let error = GetLastError();
Log!(error);
}
c++
void main() {
// The order ID is of type TId, so a string cannot be passed in; here we place an order that does not conform to the exchange's specifications to trigger an error
exchange.GetOrder(exchange.Buy(1, 1));
auto error = GetLastError();
Log(error);
}Returns
| Type | Description |
string | The most recent error message. |
Remarks
This function does not work in the backtesting system.
GetCommand
Get the strategy's interactive command.
GetCommand()Examples
-
Detect interactive commands, and when an interactive command is detected, use the
Logfunction to output it.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)rustfn main() { loop { // Rust's GetCommand() requires a timeout parameter (milliseconds) and returns Option<String>, which is None when there is no command if let Some(cmd) = GetCommand(0) { Log!(cmd); } Sleep(1000); } }c++void main() { while(true) { auto cmd = GetCommand(); if(cmd != "") { Log(cmd); } Sleep(1000); } } -
For example, in the strategy's interactive controls, add a control without an input box, name it
buy, with the control descriptionBuy; this is a button control. Then add a control with an input box, name itsell, with the control descriptionSell; this is an interactive control composed of a button and an input box. Write interactive code in the strategy to respond to the different interactive 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)rustfn main() { loop { LogStatus!(_D(None)); if let Some(cmd) = GetCommand(0) { Log!("cmd:", cmd); let arr: Vec<&str> = cmd.split(':').collect(); 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); } }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
This function is invalid in the backtesting system.
GetMeta
Get the Meta value written when generating the strategy registration code.
GetMeta()Examples
Application scenario example: Use Meta to limit the number of assets the strategy can operate.
javascript
function main() {
// The maximum asset value of the quote currency allowed by the strategy
var maxBaseCurrency = null
// Get the metadata when creating the registration code
var level = GetMeta()
// Check the condition 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 the asset value
var acc = exchange.GetAccount()
if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
// Stop executing the strategy trading logic
LogStatus(_D(), "level:", level, "Position exceeds registration code limit, strategy trading logic will not execute!")
continue
}
// Other trading logic
// Normally output the status bar information
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
# Normally output the status bar information
LogStatus(_D(), "level:", level, "Strategy running normally! ticker data:\n", ticker)
rust
fn main() {
// The maximum asset value of the quote currency allowed by the strategy
let maxBaseCurrency;
// Get the metadata when creating the registration code, Rust's GetMeta() returns a JsonValue type
let meta = GetMeta();
let level = meta.as_str().unwrap_or("");
// Check the condition corresponding to Meta
if level == "level1" {
// -1 means no limit
maxBaseCurrency = -1.0;
} else if level == "level2" {
maxBaseCurrency = 10.0;
} else if level == "level3" {
maxBaseCurrency = 1.0;
} else {
maxBaseCurrency = 0.5;
}
loop {
Sleep(1000);
let ticker = exchange.GetTicker(None).unwrap();
// Check the asset value
let acc = exchange.GetAccount().unwrap();
if maxBaseCurrency != -1.0 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks {
// Stop executing the strategy trading logic
LogStatus!(_D(None), "level:", level, "Position exceeds registration code limit, strategy trading logic will not execute!");
continue;
}
// Other trading logic
// Normally output the status bar information
LogStatus!(_D(None), "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 the strategy trading logic
LogStatus(_D(), "level:", level, "Position exceeds registration code limit, strategy trading logic will not execute!");
continue;
}
// Other trading logic
// Normally output the status bar information
LogStatus(_D(), "level:", level, "Strategy running normally! ticker data:\n", ticker);
}
}Returns
| Type | Description |
string |
|
Remarks
Application scenario: You need to impose fund restrictions on different strategy lessees. The length of the Meta value set when generating the registration code cannot exceed 190 characters. The GetMeta() function is only supported in live trading and does not work in the backtesting system. If the metadata (Meta) is not set when generating the strategy registration code, the GetMeta() function will return an empty value.
Dial
Used for raw Socket access, supporting the tcp, udp, tls, and unix protocols. Supports 4 mainstream messaging protocols: mqtt, nats, amqp, and kafka. Also supports connecting to databases, with available databases including: sqlite3, mysql, postgres, and clickhouse.
Dial(address)
Dial(address, timeout)
Dial(address, options)Examples
-
Dial function call example:
javascriptfunction main(){ // Dial supports the tcp://, udp://, tls://, and unix:// protocols, and accepts a parameter specifying the timeout in seconds var client = Dial("tls://www.baidu.com:443") if (client) { // write can take an additional numeric parameter to specify a timeout, and returns the number of bytes successfully sent client.write("GET / HTTP/1.1\nConnection: Closed\n\n") while (true) { // read can take an additional numeric parameter to specify a timeout, in milliseconds; returning null indicates an error, timeout, or that the socket has been 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()rustfn main() { // Dial supports the tcp://, udp://, tls://, and unix:// protocols, and you can use Dial::new(addr, timeout) to specify the timeout in seconds let mut client = Dial("tls://www.baidu.com:443"); if client.Valid() { // The second numeric parameter of write is used to specify a timeout, and it returns the number of bytes successfully sent client.write("GET / HTTP/1.1\nConnection: Closed\n\n", 0); loop { // The numeric parameter of read is used to specify a timeout, in milliseconds; returning an empty string indicates an error, timeout, or that the socket has been closed let buf = client.read(0); if 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(); } } -
Access Binance's WebSocket market data interface:
javascriptfunction main() { LogStatus("Connecting...") // Access Binance's 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 received after read is called 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()rustfn main() { LogStatus!("Connecting..."); // Access Binance's WebSocket interface let mut client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr"); if !client.Valid() { Log!("Connection failed, exiting"); return; } loop { // read only returns data received after read is called let buf = client.read(0); if buf == "" { break; } let obj = JSONParse(&buf).unwrap(); // The Rust SDK has no JSON serialization; here we use string concatenation to build the JSON text for the status bar table let mut rows = String::new(); if let Some(arr) = obj.as_array() { for ticker in arr { if !rows.is_empty() { rows += ","; } rows += &format!(r#"["{}","{}","{}","{}","{}","{}","{}","{}"]"#, ticker["s"].as_str().unwrap_or(""), ticker["h"].as_str().unwrap_or(""), ticker["l"].as_str().unwrap_or(""), ticker["b"].as_str().unwrap_or(""), ticker["a"].as_str().unwrap_or(""), ticker["c"].as_str().unwrap_or(""), ticker["q"].as_str().unwrap_or(""), _D(ticker["E"].as_i64().unwrap_or(0))); } } let table = format!(r#"{{"type":"table","title":"Market Chart","cols":["Symbol","High","Low","Bid","Ask","Last Price","Volume","Update Time"],"rows":[{}]}}"#, rows); LogStatus!(format!("`{}`", 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 the 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)rustfn main() { // In Rust, use Dial::with_options() and pass options as a JSON string to set the request headers let options = r#"{"headers": {"X-MBX-APIKEY": "your access key"}}"#; let random = format!("fmz{}", UnixNano()); let ts = Unix() * 1000; let secretKey = "your secret key"; let topic = "com_announcement_en"; let payload = format!("random={}&topic={}&recvWindow=30000×tamp={}", random, topic, ts); let signature = Encode("sha256", "string", "hex", &payload, "string", secretKey); let query = format!("?{}&signature={}", payload, signature); Log!("query:", query); let mut conn = Dial::with_options(&format!("wss://api.binance.com/sapi/wss{}", query), options); for _i in 0..10 { let ret = conn.read(0); Log!(ret); } }c++// Not supported yet -
Access OKX's 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 enable reconnection mode, and specify payload as the message to be sent upon reconnection. When the WebSocket connection is disconnected, it will automatically reconnect and automatically send this 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")rustfn main() { let param = r#"{"op":"subscribe","args":[{"channel":"tickers","instId":"BTC-USDT"}]}"#; // When calling the Dial function, specify reconnect=true to enable reconnection mode, and specify payload as the message to be sent upon reconnection. When the WebSocket connection is disconnected, it will automatically reconnect and automatically send this message let mut ws = Dial(&format!("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload={}", param)); if ws.Valid() { let pingCyc = 1000 * 20; let mut lastPingTime = Unix() * 1000; loop { let nowTime = Unix() * 1000; let ret = ws.read(0); Log!("ret:", ret); if nowTime - lastPingTime > pingCyc { let retPing = ws.write("ping", 0); lastPingTime = nowTime; Log!("Sending: ping", "#FF0000"); } LogStatus!("Current time:", _D(None)); Sleep(1000); } } // In Rust, the connection object is automatically closed when it goes out of scope; you can also explicitly call ws.close() }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"); } -
Access the Huobi exchange'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 the 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 the 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()")rustfn main() { let param = r#"{"sub":"market.btcusdt.detail","id":"id1"}"#; let mut ws = Dial(&format!("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload={}", param)); if ws.Valid() { loop { let ret = ws.read(0); Log!("ret:", ret); // Respond to the heartbeat packet; in Rust use JSONParse() to parse, which returns None on parse failure if let Some(jsonRet) = JSONParse(&ret) { if jsonRet["ping"].is_number() { let strPong = format!(r#"{{"pong":{}}}"#, jsonRet["ping"].as_i64().unwrap_or(0)); ws.write(&strPong, 0); Log!("Responding to ping, sending pong:", strPong, "#FF0000"); } } LogStatus!("Current time:", _D(None)); Sleep(1000); } } // In Rust the connection object is automatically closed when it leaves scope; you can also explicitly call 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 the 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()") } -
Access OKX's WebSocket authentication interface:
javascriptfunction getLogin(pAccessKey, pSecretKey, pPassphrase) { // Signature function, used to generate the login request 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 has a timeout set, timeout errors need to be filtered, otherwise redundant error output will be produced SetErrorFilter("timeout") // Subscription information for the positions channel 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) // You cannot subscribe to private channels immediately after login; you need to wait for the 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) } // Reconnect after detecting a disconnection 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)rustfn getLogin(pAccessKey: &str, pSecretKey: &str, pPassphrase: &str) -> String { // Signature function, used to generate the login request. There is no exchange.Encode member function in Rust, so the global Encode function is used to compute the signature let ts = format!("{}", Unix()); let sign = Encode("sha256", "string", "base64", &format!("{}GET/users/self/verify", ts), "string", pSecretKey); format!(r#"{{"op":"login","args":[{{"apiKey":"{}","passphrase":"{}","timestamp":"{}","sign":"{}"}}]}}"#, pAccessKey, pPassphrase, ts, sign) } fn main() { // Since the read function has a timeout set, timeout errors need to be filtered, otherwise redundant error output will be produced SetErrorFilter("timeout"); // Subscription information for the positions channel let posSubscribe = r#"{"op":"subscribe","args":[{"channel":"positions","instType":"ANY"}]}"#; let accessKey = "xxx"; let secretKey = "xxx"; let passphrase = "xxx"; let mut client_private = Dial("wss://ws.okx.com:8443/ws/v5/private"); client_private.write(&getLogin(accessKey, secretKey, passphrase), 0); Sleep(3000); // You cannot subscribe to private channels immediately after login; you need to wait for the server response client_private.write(posSubscribe, 0); if client_private.Valid() { let mut lastPingTS = Unix() * 1000; loop { let buf = client_private.read(-1); if buf != "" { Log!(buf); } // Reconnect after detecting a disconnection if buf == "" && client_private.write(posSubscribe, 0) == 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), 0); Sleep(3000); client_private.write(posSubscribe, 0); } // Send heartbeat packet let nowPingTS = Unix() * 1000; if nowPingTS - lastPingTS > 10 * 1000 { client_private.write("ping", 0); lastPingTS = nowPingTS; } } } }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 push 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// Omittedrustfn main() { let accessKey = "your accessKey"; let ts = Unix() * 1000; // Rust does not have the exchange.Encode member function and cannot use the {{secretkey}} template substitution, so use the global Encode function to pass the secret key directly to compute the signature let signature = Encode("sha256", "string", "hex", &format!("{}", ts), "string", "your secretKey"); Log!("signature:", signature); // The Rust SDK does not have JSON serialization, so use string concatenation to construct the payload's JSON text let payload = format!(r#"{{"id":1,"method":"server.sign","params":{{"access_id":"{}","signed_str":"{}","timestamp":{}}}}}"#, accessKey, signature, ts); Log!("payload:", payload); let mut conn = Dial(&format!("wss://socket.coinex.com/v2/futures|compress=gzip&mode=recv&payload={}", payload)); if !conn.Valid() { Panic!("stop"); } Log!("Dial ... ", conn.read(0)); // Subscribe to position push conn.write(r#"{"method":"position.subscribe","params":{"market_list":["BTCUSDT"]},"id":1}"#, 0); loop { let msg = conn.read(0); if msg != "" { Log!("msg:", msg); } } }c++// Omitted -
The following example demonstrates how to access the
Websocketinterface of the MEXC exchange, subscribe to thepublic.aggre.deals.v3.api.pbchannel, and useprotobuf.jsto decode the 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 when the Dial function connects to a database has 2 unique methods:
-
exec(sqlString): Used to execute SQL statements, with usage similar to theDBExec()function.-
fd(): This function returns a handle (for example, a handle variable named handle), used for reconnecting in other threads. Even if the connection object created by Dial has already been closed via theclose()function, you can still pass this handle into theDial()function (for example,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 an 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 the 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 supportedrustfn main() { // let mut client = Dial("sqlite3://:memory:"); // Use an in-memory database let mut client = Dial("sqlite3://test1.db"); // Open/connect to the database file in the docker's directory // Rust's connection object does not support the fd() method // Query the tables in the database let ret = client.exec("SELECT name FROM sqlite_master WHERE type='table'"); Log!(format!("{:?}", ret)); Log!("Executing client.close()"); client.close(); }c++// Not supported
Returns
| Type | Description | ||||||||||||||||||||
object | If the call times out, the
For data pushed via the WebSocket protocol, if the time interval between two calls of the
|
Arguments
| Name | Type | Required | Description |
address | string | Yes | The request address. |
timeout | number | No | The timeout period (unit: seconds). |
options | object | No | Configuration options. |
Remarks
address parameter details: after the standard 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 instead. The portion after the separator specifies the functional parameter settings, with individual parameters joined by the & character.
For example, to set both an ss5 proxy and compression parameters at the same time, 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=value | compress specifies the compression method. Available values include gzip_raw, gzip, etc. If the gzip used is not standard gzip, you can use the extended form: gzip_raw |
| Parameters related to WebSocket protocol data compression: mode=value | mode specifies the compression mode, with three options: dual, send, and recv. dual indicates bidirectional compression, i.e., sending and receiving compressed data simultaneously; send indicates only sending compressed data; recv indicates only receiving compressed data and decompressing it locally. |
| Enable the WebSocket protocol compression setting: enableCompression=true | Use enableCompression=false to disable this setting. It is disabled by default. |
| Parameters for configuring underlying auto-reconnection of the WebSocket protocol: reconnect=value | reconnect sets whether to auto-reconnect. reconnect=true enables reconnection. If this parameter is not set, reconnection is disabled by default. |
| Parameters for configuring underlying auto-reconnection of the WebSocket protocol: interval=value | interval is the retry interval, in milliseconds. For example, interval=10000 means a retry interval of 10 seconds; when not set, it defaults to 1 second, i.e., interval=1000. |
| Parameters for configuring underlying auto-reconnection of the WebSocket protocol: payload=value | payload is the subscription message to be sent when the WebSocket reconnects, for example: payload=okok. |
| Parameters related to the socks5 proxy: proxy=value | proxy configures the ss5 proxy. The value format is: socks5://name:[email protected]:1080. Here name is the username of the ss5 server, pwd is the login password of the ss5 server, and 1080 is the port of the ss5 service. |
The Dial() function is only supported in live trading.
When using the Dial function to connect to a database, you can refer to the Go language driver project corresponding to each database for how to write the connection string.
| Supported databases | Driver project | Connection String | Notes |
|---|---|---|---|
| sqlite3 | github.com/mattn/go-sqlite3 | sqlite3://file:test.db?cache=shared&mode=memory | The sqlite3:// prefix indicates that the sqlite3 database is used. Example call: 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 how the Dial function parses the address parameter. See the example below.
Example of calling the backPack exchange websocket private interface:
javascript
var client = null
function main() {
// The 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 a base64 encoding, it may contain the character "="
var signature = signEd25519(data)
// After being JSON-encoded, payload may contain the character "="
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}}")
}
Using the following calling approach in the code works properly:
javascript
client = Dial("wss://ws.backpack.exchange")
client.write(JSON.stringify(payload))
If it is written directly into the payload (in the address), it will not work properly, for example:
javascript
client = Dial("wss://ws.backpack.exchange|payload=" +
JSON.stringify(payload))
Currently, only the JavaScript language supports using the mqtt, nats, amqp, and kafka communication protocols in the Dial function. The following uses JavaScript strategy code as an example to demonstrate how to use the four protocols mqtt, nats, amqp, and kafka:
javascript
// You need to first configure and deploy the proxy servers for each protocol
// For ease of demonstration, both subscribing to (read operation) and publishing to (write operation) the topic test_topic are performed within this current 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 a detailed introduction, please refer to the documentation: Exploring FMZ: Practices of Communication Protocols Between Live Trading Strategies
HttpQuery
Sends an HTTP request.
HttpQuery(url)
HttpQuery(url, options)Examples
-
An example of accessing the OKX public market data API interface.
javascriptfunction main(){ // An example of a GET request without parameters var info = JSON.parse(HttpQuery("https://www.okx.com/api/v5/public/time")) Log(info) // An example of a GET request with parameters 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 does not support Python; you can use the urllib/urllib2 library instead 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)rustfn main() { // An example of a GET request without parameters; in Rust, the return value type annotation determines whether the Body string (String) or the complete response (HttpRet) is returned let body: String = HttpQuery("https://www.okx.com/api/v5/public/time", None); let info = JSONParse(&body).unwrap(); Log!(info); // An example of a GET request with parameters let body2: String = HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT", None); let ticker = JSONParse(&body2).unwrap(); 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); } -
An example of using proxy settings with the HttpQuery function.
javascriptfunction main() { // This sets a proxy and sends an HTTP request, with no username and no password; this HTTP request will be sent through the proxy HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/") // This sets a proxy and sends an HTTP request, providing a username and password, effective only for the current call to HttpQuery; subsequent calls to HttpQuery("http://www.baidu.com") will not use the proxy HttpQuery("socks5://username:[email protected]:8889/http://www.baidu.com/") }python# HttpQuery does not support Python; you can use Python's urllib2 libraryrustfn main() { // This sets a proxy and sends an HTTP request, with no username and no password; this HTTP request will be sent through the proxy let ret1: String = HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/", None); // This sets a proxy and sends an HTTP request, providing a username and password, effective only for the current call to HttpQuery; subsequent calls to HttpQuery("http://www.baidu.com") will not use the proxy let ret2: String = HttpQuery("socks5://username:[email protected]:8889/http://www.baidu.com/", None); }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 | The URL of the HTTP request. |
options | object | No | Settings related to the HTTP request, for example the following structure:
All fields in this structure are optional; for example, the |
See Also
Remarks
The HttpQuery() function only supports the JavaScript and C++ languages; in Python, you can use the urllib library to send HTTP requests directly. HttpQuery() is mainly used to access exchange interfaces that do not require signing, 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, the number of times different URLs can be accessed is limited to 20, and HttpQuery() caches the accessed data; on the second access to the same URL, the HttpQuery() function returns the cached data instead of making 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 according to the parameters passed in.
Encode(algo, inputFormat, outputFormat, data)
Encode(algo, inputFormat, outputFormat, data, keyFormat, key)Examples
-
Example of calling the Encode function.
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")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84barustfn main() { // In Rust, all 6 parameters of the Encode() function are required; when not encrypting, simply pass empty strings for keyFormat and key 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 }c++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", which are used to encode and decode 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)rustfn main() { // In Rust, all 6 parameters of the Encode() function are required; when not encrypting, pass empty strings for keyFormat and key let ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好", "", ""); // e4bda0e5a5bd Log!(ret1); let ret2 = Encode("text.decoder.utf8", "hex", "string", &ret1, "", ""); Log!(ret2); let ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好", "", ""); // c4e3bac3 Log!(ret3); let 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 The The |
inputFormat | string | Yes | Used to specify the data format of the |
outputFormat | string | Yes | Used to specify the output data format. The |
data | string | Yes | The |
keyFormat | string | No | Used to specify the data format of the |
key | string | No | The When the When the |
Remarks
The Encode() function is only supported in live trading. If the key and keyFormat parameters are not passed in, no key encryption is performed.
UnixNano
Get the nanosecond-level timestamp of the current moment.
UnixNano()Examples
If you need to get a millisecond-level timestamp, you can use the following code:
javascript
function main() {
var time = UnixNano() / 1000000
Log(_N(time, 0))
}
python
def main():
time = UnixNano()
Log(time)
rust
fn main() {
let time = UnixNano() / 1000000;
Log!(_N(time, 0));
}
c++
void main() {
auto time = UnixNano();
Log(time);
}Returns
| Type | Description |
number | The |
See Also
Unix
Get the second-level timestamp of the current moment.
Unix()Examples
javascript
function main() {
var t = Unix()
Log(t)
}
python
def main():
t = Unix()
Log(t)
rust
fn main() {
let t = Unix();
Log!(t);
}
c++
void main() {
auto t = Unix();
Log(t);
}Returns
| Type | Description |
number | Returns the second-level timestamp. |
See Also
GetOS
Retrieves the operating system information of the device hosting the bot.
GetOS()Examples
javascript
function main() {
Log("GetOS:", GetOS())
}
python
def main():
Log("GetOS:", GetOS())
rust
fn main() {
Log!("GetOS:", GetOS());
}
c++
void main() {
Log("GetOS:", GetOS());
}Returns
| Type | Description |
string | Operating system information. |
Remarks
For example, a bot running on the Mac OS operating system may return darwin/amd64 when calling the GetOS() function. Since Apple computers use various hardware architectures, the return value includes the specific architecture information. Here, darwin is the kernel name of the Mac OS system.
MD5
Calculate the MD5 hash of the parameter data.
MD5(data)Examples
javascript
function main() {
Log("MD5", MD5("hello world"))
}
python
def main():
Log("MD5", MD5("hello world"))
rust
fn main() {
Log!("MD5", MD5("hello world"));
}
c++
void main() {
Log("MD5", MD5("hello world"));
}Returns
| Type | Description |
string | The MD5 hash value. |
Arguments
| Name | Type | Required | Description |
data | string | Yes | The data on which to perform the MD5 calculation. |
See Also
Remarks
After calling the MD5("hello world") function, the return value is: 5eb63bbbe01eeed093cb22bb8f5acdc3.
DBExec
Database interface function.
DBExec(sql)Examples
-
Supports in-memory databases. For the parameter of the
DBExecfunction, if the sql statement begins with:, the operation is executed in the in-memory database; since there is no need to write to a file, it is faster. This approach is suitable for database operations that do not 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) // Add a record Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) // Query data 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) # Add a record Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) # Query data Log(DBExec(":SELECT * FROM TEST_TABLE;"))rustfn main() { let 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)", ]; let strSql = arr.join(""); let ret = DBExec(&strSql); Log!(format!("{:?}", ret)); // Add a record Log!(format!("{:?}", DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))); // Query data Log!(format!("{:?}", DBExec(":SELECT * FROM TEST_TABLE;"))); }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); // Add a record Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")); // Query data Log(DBExec(":SELECT * FROM TEST_TABLE;")); } -
Use the
DBExec()function to create a data 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)rustfn main() { let 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)", ]; let strSql = arr.join(""); let ret = DBExec(&strSql); Log!(format!("{:?}", 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); } -
Perform insert, delete, query, and update operations on records in a data 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("") Log(DBExec(strSql)) // Insert a record Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) // Query data Log(DBExec("SELECT * FROM TEST_TABLE;")) // Update data Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000)) // Delete data 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)) # Insert a record Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) # Query data Log(DBExec("SELECT * FROM TEST_TABLE;")) # Update data Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000)) # Delete data Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))rustfn main() { let 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)", ]; let strSql = arr.join(""); Log!(format!("{:?}", DBExec(&strSql))); // Insert a record Log!(format!("{:?}", DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))); // Query data Log!(format!("{:?}", DBExec("SELECT * FROM TEST_TABLE;"))); // Update data. Rust's DBExec() function only accepts a single SQL statement string argument and does not support the ? placeholder for passing parameters; parameter values are written directly in the statement Log!(format!("{:?}", DBExec("UPDATE TEST_TABLE SET HIGH=110 WHERE TS=1518970320000;"))); // Delete data Log!(format!("{:?}", 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)); // Insert a record Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")); // Query data Log(DBExec("SELECT * FROM TEST_TABLE;")); // Update data Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000)); // Delete data Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110)); }
Returns
| Type | Description |
object | An object containing the execution result of the sql statement, for example: ```json {"columns":["TS","HIGH","OPEN","LOW","CLOSE","VOLUME"],"values":[[1518970320000,100,99.1,90,100,12345.6]]} ``` |
Arguments
| Name | Type | Required | Description |
sql | string | Yes | The sql statement string. |
See Also
Remarks
-
By passing an argument to the
DBExec()function, you can operate on the live trading database (SQLite database).-
It supports insert, delete, query, and update operations on data in the live trading database, and supports SQLite syntax.
-
The system-reserved tables in the live trading database include:
kvdb,cfg,log,profit,chart. Please do not operate on these tables. -
Transactions are currently not supported, and such operations are not recommended, as they may cause system conflicts.
-
The
DBExec()function only supports live trading.
-
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)
rust
fn main() {
let uuid1 = UUID();
let uuid2 = UUID();
Log!(uuid1, uuid2);
}
c++
void main() {
auto uuid1 = UUID();
auto uuid2 = UUID();
Log(uuid1, uuid2);
}Returns
| Type | Description |
string | A 32-bit UUID. |
Remarks
The UUID() function is only supported in live trading.
EventLoop
Listens for events and returns when any WebSocket has readable data, or when concurrent tasks such as exchange.Go() or HttpQuery_Go() complete.
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 a Sleep statement is used here, it will cause the subsequent EventLoop function to miss the previous events. Because after waiting 2 seconds, the concurrent functions have already received data, and only then does the EventLoop listening mechanism start, so these events will be missed
// Unless EventLoop(-1) is called on the very first line to initialize the EventLoop listening mechanism first, these events will not be missed
// Log("GetDepth:", routine_getDepth.wait()) If the wait function is called here in advance to retrieve the result of the concurrent GetDepth function call, the event of this 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())
rust
fn main() {
// In Rust, exchange.Go uses typed tokens (such as Go::GetTicker) instead of method-name strings; pass () when there are no arguments
let routine_getTicker = exchange.Go(Go::GetTicker, ());
let routine_getDepth = exchange.Go(Go::GetDepth, ());
let routine_getTrades = exchange.Go(Go::GetTrades, ());
// Sleep(2000), if a Sleep statement is used here, it will cause the subsequent EventLoop function to miss the previous events. Because after waiting 2 seconds, the concurrent functions have already received data, and only then does the EventLoop listening mechanism start, so these events will be missed
// Unless EventLoop(-1) is called on the very first line to initialize the EventLoop listening mechanism first, these events will not be missed
// Log!("GetDepth:", routine_getDepth.wait(0)) If the wait function is called here in advance to retrieve the result of the concurrent GetDepth function call, the event of this GetDepth function receiving the request result will not be returned in the EventLoop function
let ts1 = Unix() * 1000;
let ret1 = EventLoop(0);
let ts2 = Unix() * 1000;
let ret2 = EventLoop(0);
let ts3 = Unix() * 1000;
let 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(0).unwrap());
Log!("GetDepth:", routine_getDepth.wait(0).unwrap());
Log!("GetTrades:", routine_getTrades.wait(0).unwrap());
}
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 empty, the
|
Arguments
| Name | Type | Required | Description |
timeout | number | No | The When |
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 first called after an event callback has already occurred, that earlier event will be missed. The queue structure encapsulated at the system's underlying level can cache at most 500 event callbacks; if the program does not call the EventLoop() function in time to retrieve them, later event callbacks exceeding the 500-cache limit will be lost.
Calling the EventLoop() function does not affect the underlying WebSocket cache queue of the system, nor does it affect the cache of concurrent functions such as exchange.Go(). The data in these caches must still be retrieved using their respective methods. For data that has already been retrieved before the EventLoop() function returns, no return event will be generated again in the EventLoop() function.
The main purpose of the EventLoop() function is to notify the strategy layer that the system's underlying level has received new network data, thereby driving the entire strategy in an event-driven manner. When the EventLoop() function returns an event, you only need to iterate through all data sources (such as WebSocket connections and objects created by exchange.Go()) and attempt to retrieve the data.
The EventLoop() function is only supported in live trading.
When called in the main function main(), it listens for events on the main thread. In strategies written in JavaScript, it can also be called in the execution function of a thread created by the threading.Thread() function to listen for events on 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 store data. This function implements a persistently stored global dictionary, saving data as key-value (KV) pairs permanently in the local database file of the hosting device (docker).
_G()
_G(k)
_G(k, v)Examples
javascript
function main(){
// Set a global variable num with a value of 1
_G("num", 1)
// Change the global variable num to the string value ok
_G("num", "ok")
// Delete the global variable num
_G("num", null)
// Return the value of the global variable num
Log(_G("num"))
// Delete all global variables
_G(null)
// Return the live trading bot ID
var robotId = _G()
}
python
def main():
_G("num", 1)
_G("num", "ok")
_G("num", None)
Log(_G("num"))
_G(None)
robotId = _G()
rust
fn main() {
// Set a global variable num with a value of 1
_G!("num", 1);
// Change the global variable num to the string value ok
_G!("num", "ok");
// Delete the global variable num
_G!("num", null);
// Return the value of the global variable num
Log!(_G!("num"));
// Rust does not support the _G!(null) form for deleting all global variables
// Return the live trading bot ID
let robotId = _G!();
}
c++
void main() {
_G("num", 1);
_G("num", "ok");
_G("num", NULL);
Log(_G("num"));
_G(NULL);
// auto robotId = _G(); is not supported
}Returns
| Type | Description |
string / number / bool / object / array / null | The value data in the persistently stored |
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 bot corresponds to a separate database. After the strategy restarts or the hosting device (docker) stops running, the data saved by the _G() function will still persist. However, after a backtest ends, the data saved by the _G() function in the backtesting system will be cleared. When using the _G() function to persistently store data, use it reasonably according to the memory and disk space of the hardware device, and never abuse it.
In live trading, when the _G() function is called without passing any parameters, the _G() function returns the Id of the current live trading bot.
When calling the _G() function, passing a null value for the parameter v indicates deleting the corresponding k-v key-value pair.
When calling the _G() function, if only the parameter k is passed as a string, then the _G() function returns the stored value corresponding to the parameter k.
When calling the _G() function, if only the parameter k is passed as a null value, it indicates deleting all recorded k-v key-value pairs.
After a k-v key-value pair has been persistently stored, calling the _G() function again and passing the persistently stored key name as the parameter k and a new value as the parameter v will update that k-v key-value pair.
Taking a live trading bot with Id 123456 as an example, the K-V key-value data persistently stored using the _G() function is stored in the /logs/storage/123456/123456.db3 database file located in the directory of the hosting device (docker) to which the live trading bot (i.e., the strategy instance program) belongs, and the data is recorded in the kvdb table.
_D
Convert a millisecond-level timestamp or a Date object into a time string.
_D()
_D(timestamp)
_D(timestamp, fmt)Examples
-
Get and print the current time string:
javascriptfunction main(){ var time = _D() Log(time) }pythondef main(): strTime = _D() Log(strTime)rustfn main() { let time = _D(None); Log!(time); }c++void main() { auto strTime = _D(); Log(strTime); } -
The timestamp is 1574993606000; convert it with code:
javascriptfunction main() { Log(_D(1574993606000)) }pythondef main(): # Running on a server set to Beijing time, the result is: 2019-11-29 10:13:26; while running this code on a docker on a server in another region gives the result: 2019-11-29 02:13:26 Log(_D(1574993606))rustfn main() { Log!(_D(1574993606000)); }c++void main() { Log(_D(1574993606000)); } -
Format using the
fmtargument. The format strings forJavaScript,Python, andC++differ; please refer to 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--26rustfn main() { // Rust's _D() function does not support the fmt argument; it only supports the default format: yyyy-MM-dd hh:mm:ss Log!(_D(1574993606000)); // 2019-11-29 10:13:26 }c++void main() { Log(_D(1574993606000, "%Y--%m--%d %H--%M--%S")); // 2019--11--29 10--13--26 }
Returns
| Type | Description |
string | The time string. |
Arguments
| Name | Type | Required | Description |
timestamp | number / object | No | A millisecond-level timestamp or a |
fmt | string | No | The format string. Default format for |
See Also
Remarks
If no argument is passed, the current time string is returned. When using the _D() function in a Python strategy, note that the argument passed in is a second-level timestamp (in JavaScript and C++ strategies it is a millisecond-level timestamp; 1 second equals 1000 milliseconds). When using the _D() function in live trading to parse a timestamp into a readable time string, note the time zone and time settings of the operating system on which the docker (hosting program) runs, because the parsing result of the _D() function depends on the docker system's time.
_N
Format a floating-point number.
_N()
_N(num)
_N(num, precision)Examples
-
For example,
_N(3.1415, 2)keeps3.1415to two decimal places, removes the remaining digits, 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)rustfn main() { let i = 3.1415; Log!(i); let ii = _N(i, 2); Log!(ii); }c++void main() { auto i = 3.1415; Log(i); auto ii = _N(i, 2); Log(ii); } -
If you need to set the N digits to the left of the decimal point all to 0, you can write it like this:
javascriptfunction main(){ var i = 1300 Log(i) var ii = _N(i, -3) // Check the log and you will see it is 1000 Log(ii) }pythondef main(): i = 1300 Log(i) ii = _N(i, -3) Log(ii)rustfn main() { let i = 1300; Log!(i); let ii = _N(i, -3); // Check the log and you will see it is 1000 Log!(ii); }c++void main() { auto i = 1300; Log(i); auto ii = _N(i, -3); Log(ii); }
Returns
| Type | Description |
number | The floating-point number formatted according to the precision setting. |
Arguments
| Name | Type | Required | Description |
num | number | Yes | The floating-point number to be formatted. |
precision | number | No | Used to set the formatting precision. The parameter |
See Also
Remarks
The parameter precision can be a positive integer or a negative integer.
_C
A retry function used for fault-tolerant handling of interface calls.
_C(pfn)
_C(pfn, ...args)Examples
-
Apply fault-tolerant handling to a function without parameters:
javascriptfunction main(){ var ticker = _C(exchange.GetTicker) // Change the retry interval of the _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)rustfn main() { let ticker = _C!(exchange.GetTicker(None)); // Change the retry interval of the _C!() macro to 2 seconds _CDelay(2000); let depth = _C!(exchange.GetDepth(None)); Log!(ticker); Log!(depth); }c++void main() { auto ticker = _C(exchange.GetTicker); _CDelay(2000); auto depth = _C(exchange.GetDepth); Log(ticker); Log(depth); } -
Apply fault-tolerant handling to a function with parameters:
javascriptfunction main(){ var records = _C(exchange.GetRecords, PERIOD_D1) Log(records) }pythondef main(): records = _C(exchange.GetRecords, PERIOD_D1) Log(records)rustfn main() { let records = _C!(exchange.GetRecords(None, PERIOD_D1, None)); Log!(records); }c++void main() { auto records = _C(exchange.GetRecords, PERIOD_D1); Log(records); } -
It can also be used to apply fault-tolerant handling to 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)rustfn test(a: i64, b: i64) -> Result<bool> { let time = Unix(); if time % b == 3 { Log!("Condition met!", "#FF0000"); return Ok(true); } Log!("Retrying!", "#FF0000"); Err(Error::Api("retry".to_string())) } fn main() { // In Rust, a custom function can use the _C! macro for fault tolerance as long as it returns a Result type; it will retry when Err is returned let ret = _C!(test(1, 5)); Log!(ret); }c++// C++ does not support this way of applying fault tolerance to custom functions
Returns
| Type | Description |
All types supported by the platform except false values and null values (any). | The return value after 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 | The parameters of the callback function. There can be multiple |
Remarks
The _C() function repeatedly calls the specified function until it returns successfully (when the function referenced by the parameter pfn returns a null value or a false value upon being called, the call to pfn will be retried).
For example, _C(exchange.GetTicker). The default retry interval is 3 seconds, and 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.
Fault-tolerant handling can be applied to the following functions (but is not limited to them):
exchange.GetTicker()exchange.GetDepth()exchange.GetTrades()exchange.GetRecords()exchange.GetAccount()exchange.GetOrders()exchange.GetOrder()exchange.GetPositions()
All of the above functions can be called through the _C() function to achieve fault tolerance. The fault tolerance of the _C() function is not limited to the functions listed above. Please note that the parameter pfn is a function reference rather than a function call, i.e. it should be written as _C(exchange.GetTicker), not _C(exchange.GetTicker()).
_Cross
Returns the number of crossover periods between array arr1 and array arr2.
_Cross(arr1, arr2)Examples
You can simulate a set of data to test the _Cross(Arr1, Arr2) function:
javascript
// Fast line indicator
var arr1 = [1,2,3,4,5,6,8,8,9]
// Slow line indicator
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))
rust
fn main() {
// Fast line indicator
let arr1 = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 8.0, 9.0];
// Slow line indicator
let arr2 = [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 7.0, 7.0, 7.0];
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 crossover periods between array |
Arguments
| Name | Type | Required | Description |
arr1 | array | Yes | An array whose elements are of type |
arr2 | array | Yes | An array whose elements are of type |
Remarks
When the return value of the _Cross() function is positive, it indicates the number of periods since the upward cross (golden cross); when negative, it indicates the number of periods since the downward cross (death cross); when 0, it indicates that the current prices are equal. For detailed usage instructions, please refer to: Analysis and Usage Instructions for the Built-in Function _Cross.
JSON.parse
The JSON.parse function is a method of the ECMAScript standard built-in object JSON, used to decode (parse) a JSON string. The FMZ Quant Trading Platform has extended it with an additional parameter safeStr on this basis.
JSON.parse(s)
JSON.parse(s, safeStr)Examples
Decode (parse) a JSON string containing a large numeric value.
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
# You can use Python's third-party libraries to handle large numeric data.
rust
fn main() {
// Rust uses the JSONParse() function to parse JSON strings, without the safeStr parameter
// Large numeric values that exceed the precision range will be parsed as f64, which may lose precision
let s1 = r#"{"num": 8754613216564987646512354656874651651358}"#;
Log!("JSONParse:", JSONParse(s1).unwrap()["num"].as_f64().unwrap_or(0.0)); // JSONParse: 8.754613216564987e39
let s2 = r#"{"num": 123}"#;
Log!("JSONParse:", JSONParse(s2).unwrap()["num"].as_f64().unwrap_or(0.0)); // JSONParse: 123
}
c++
// Other solutions can be used to handle this.Returns
| Type | Description |
object | The return value is a |
Arguments
| Name | Type | Required | Description |
s | string | Yes | This parameter is 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 the string type.
The safeStr parameter position also supports passing in a reviver parameter, i.e. a function used to transform the result, which is called once for each member of the object; for specific usage, please refer to the relevant materials, which will not be elaborated here.
Only the JavaScript language is supported.
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 communication between live trading bots, allowing the current bot's status data to be broadcast to a channel for other live trading bots to subscribe to and retrieve.
SetChannelData(data)Examples
-
Channel Broadcaster Example - Publishing BTC Market Price Data
javascriptfunction main() { var updateId = 0 var robotId = _G() // Get current live bot ID while(true) { // Get real-time market price var ticker = exchange.GetTicker("BTC_USDT") if (!ticker) { Sleep(5000) continue } // Construct 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 the latest state on the channel (overwrites the 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 once per minute } }pythondef main(): updateId = 0 robotId = _G() # Get current live bot ID while True: # Get real-time market price ticker = exchange.GetTicker("BTC_USDT") if not ticker: Sleep(5000) continue # Construct 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 the latest state on the channel (overwrites the 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 once per minuterustfn main() { let mut updateId = 0; let robotId = _G!(); // Get current live bot ID loop { // Get real-time market price let ticker = match exchange.GetTicker("BTC_USDT") { Ok(t) => t, Err(_) => { Sleep(5000); continue; } }; // Construct current channel state data // Rust's SetChannelData only accepts a string argument, so use format! to build the JSON text updateId += 1; let timestamp = Unix() * 1000; let channelState = format!( r#"{{"robotId": {}, "updateId": {}, "timestamp": {}, "symbol": "BTC_USDT", "lastPrice": {}, "volume": {}, "high": {}, "low": {}}}"#, robotId, updateId, timestamp, ticker.Last, ticker.Volume, ticker.High, ticker.Low ); // Publish the latest state on the channel (overwrites the old state) SetChannelData(&channelState); // Display current channel state LogStatus!(format!( "Channel Broadcaster [Bot ID: {}]\nUpdate ID: #{}\nTime: {}\nSymbol: BTC_USDT\nLast Price: ${:.2}\nVolume: {:.4}\nHigh: ${:.2}\nLow: ${:.2}", robotId, updateId, _D(timestamp), ticker.Last, ticker.Volume, ticker.High, ticker.Low )); Sleep(60000); // Update channel state once per minute } }c++ -
Cross-platform sending example - Simulate an external platform (such as TradingView) sending data to an FMZ live bot
javascript// This example demonstrates how to use HttpQuery to send an HTTP POST request, simulating an external platform sending data to an FMZ live bot // In a real scenario, external platforms (such as TradingView's Webhook alert URL, third-party trading systems, etc.) directly call the FMZ API endpoint function main() { let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" let robotId = 123456 // Target live bot ID (the live bot used 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 the HTTP POST request let options = { method: "POST", body: JSON.stringify(sendData) // body can be a JSON string, plain text, etc. } let url = `${baseUrl}/api/v1?method=pub&robot=${robotId}&channel=${uuid}` // Send the data let ret = HttpQuery(url, options) Log("Simulated external platform sending data, result:", ret) Sleep(10000) // Send once every 10 seconds } }python# This example demonstrates how to use HttpQuery to send an HTTP POST request, simulating an external platform sending data to an FMZ live bot # In a real scenario, external platforms (such as TradingView's Webhook alert URL, third-party trading systems, etc.) directly call the FMZ API endpoint import json def main(): uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" robotId = 123456 # Target live bot ID (the live bot used 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 the HTTP POST request options = { "method": "POST", "body": json.dumps(sendData) # body can be a JSON string, plain text, etc. } url = "{}/api/v1?method=pub&robot={}&channel={}".format(baseUrl, robotId, uuid) # Send the data ret = HttpQuery(url, options) Log("Simulated external platform sending data, result:", ret) Sleep(10000) # Send once every 10 secondsrust// This example demonstrates how to use HttpQuery to send an HTTP POST request, simulating an external platform sending data to an FMZ live bot // In a real scenario, external platforms (such as TradingView's Webhook alert URL, third-party trading systems, etc.) directly call the FMZ API endpoint fn main() { let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30"; let robotId = 123456; // Target live bot ID (the live bot used to receive data) let baseUrl = "https://www.fmz.com"; loop { // Prepare the data to send (can be JSON, text, or other formats) let sendData = format!( r#"{{"action": "buy", "symbol": "BTC_USDT", "price": 50000, "timestamp": {}}}"#, Unix() * 1000 ); // Construct the HTTP POST request; {:?} escapes body into a valid JSON string value let options = format!(r#"{{"method": "POST", "body": {:?}}}"#, sendData); let url = format!("{}/api/v1?method=pub&robot={}&channel={}", baseUrl, robotId, uuid); // Send the data let ret: String = HttpQuery(&url, options.as_str()); Log!("Simulated external platform sending data, result:", ret); Sleep(10000); // Send once every 10 seconds } }c++
Returns
| Type | Description |
null | This function has no return value. |
Arguments
| Name | Type | Required | Description |
data | object / array / string / number / bool / null | Yes | The data to be published to the channel. It can be any data structure that supports |
See Also
Remarks
The SetChannelData() function is a non-blocking call; it returns immediately after being called and does not wait for the data transmission to complete.
Each live trading bot has its own dedicated channel, and the channel ID is the bot ID (which can be obtained via the _G() function).
The channel only stores the latest status data. Each call to SetChannelData() overwrites the previously published data rather than appending to a message history.
Channel data supports broadcasting across live trading bots, across dockers, and across servers, and multiple bots can subscribe to the same channel.
The subscriber side uses the GetChannelData() function to subscribe to channel data.
Channel communication is intended for live trading environments; this feature may be restricted in the backtesting system.
The byte length of the passed-in data parameter after JSON serialization must not exceed 1024 bytes. Exceeding this limit may cause the data publishing to fail. It is recommended to transmit only the necessary status information and to avoid transmitting overly large data objects.
The published data should be used reasonably according to the memory and network bandwidth of the hardware device; avoid publishing overly large data objects.
The data published by the SetChannelData() function can not only be subscribed to by other live trading bots within the FMZ platform, but also supports cross-platform data sending. External platforms (such as TradingView Webhook alerts, third-party trading systems, monitoring software, etc.) can send data to a specified FMZ live trading bot via HTTP POST requests.
How to send data across platforms: External systems send data to the FMZ platform API endpoint via an HTTP POST request: https://www.fmz.com/api/v1?method=pub&robot={robotId}&channel={uuid}, where robotId is the target live trading bot 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, plain text, or other formats. Note: a live trading bot must already be subscribed to the specified UUID channel before an external system can successfully send data; the broadcast data will be sent to all live trading bots under the same docker as the robotId bot, and any bot under that docker subscribed to the UUID channel can receive the data.
GetChannelData
Subscribes to the channel data of a specified live trading bot. This function is used for inter-bot communication, allowing you to retrieve the latest status data published by other live trading bots via the SetChannelData() function.
GetChannelData(channelId)Examples
-
Channel Subscriber Example - Subscribe to Channel Data from Two Live Trading Bots
javascriptfunction main() { // The two channel IDs to subscribe to (modify according to your actual situation) var channelId1 = "632799" // Live trading bot ID of channel 1 var channelId2 = "632800" // Live trading bot ID of channel 2 while(true) { // Subscribe to the current state of channel 1 var state1 = GetChannelData(channelId1) // Subscribe to the current state of channel 2 var state2 = GetChannelData(channelId2) // Build the status display var statusMsg = "Channel Subscriber - Current Subscription State\n\n" // Display channel 1 state statusMsg += "═══ Channel 1 [" + channelId1 + "] ═══\n" if (state1 !== null) { statusMsg += "Update ID: #" + state1.updateId + "\n" statusMsg += "Time: " + _D(state1.timestamp) + "\n" statusMsg += "Trading Pair: " + state1.symbol + "\n" statusMsg += "Last Price: $" + state1.lastPrice.toFixed(2) + "\n" statusMsg += "Volume: " + state1.volume.toFixed(4) + "\n" } else { statusMsg += "State: Waiting... (first call returns null)\n" } statusMsg += "\n" // Display channel 2 state statusMsg += "═══ Channel 2 [" + channelId2 + "] ═══\n" if (state2 !== null) { statusMsg += "Update ID: #" + state2.updateId + "\n" statusMsg += "Time: " + _D(state2.timestamp) + "\n" statusMsg += "Trading Pair: " + state2.symbol + "\n" statusMsg += "Last Price: $" + state2.lastPrice.toFixed(2) + "\n" statusMsg += "Volume: " + state2.volume.toFixed(4) + "\n" } else { statusMsg += "State: Waiting... (first call returns null)\n" } LogStatus(statusMsg) Sleep(5000) // Subscribe to the channel every 5 seconds } }pythondef main(): # The two channel IDs to subscribe to (modify according to your actual situation) channelId1 = "632799" # Live trading bot ID of channel 1 channelId2 = "632800" # Live trading bot ID of channel 2 while True: # Subscribe to the current state of channel 1 state1 = GetChannelData(channelId1) # Subscribe to the current state of channel 2 state2 = GetChannelData(channelId2) # Build the status display statusMsg = "Channel Subscriber - Current Subscription State\n\n" # Display channel 1 state 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 += "Trading Pair: {}\n".format(state1["symbol"]) statusMsg += "Last Price: ${:.2f}\n".format(state1["lastPrice"]) statusMsg += "Volume: {:.4f}\n".format(state1["volume"]) else: statusMsg += "State: Waiting... (first call returns None)\n" statusMsg += "\n" # Display channel 2 state 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 += "Trading Pair: {}\n".format(state2["symbol"]) statusMsg += "Last Price: ${:.2f}\n".format(state2["lastPrice"]) statusMsg += "Volume: {:.4f}\n".format(state2["volume"]) else: statusMsg += "State: Waiting... (first call returns None)\n" LogStatus(statusMsg) Sleep(5000) # Subscribe to the channel every 5 secondsrustfn main() { // Rust's GetChannelData() function does not accept a channel ID parameter; it can only read the current live trading bot's own channel // (i.e. the latest data published by this bot via SetChannelData()); it cannot subscribe to the channels of other live trading bots loop { // Subscribe to the current state of the channel let state = GetChannelData(); // Build the status display let mut statusMsg = String::from("Channel Subscriber - Current Subscription State\n\n"); if !state.is_null() { statusMsg += &format!("Update ID: #{}\n", state["updateId"].as_i64().unwrap_or(0)); statusMsg += &format!("Time: {}\n", _D(state["timestamp"].as_i64().unwrap_or(0))); statusMsg += &format!("Trading Pair: {}\n", state["symbol"].as_str().unwrap_or("")); statusMsg += &format!("Last Price: ${:.2}\n", state["lastPrice"].as_f64().unwrap_or(0.0)); statusMsg += &format!("Volume: {:.4}\n", state["volume"].as_f64().unwrap_or(0.0)); } else { statusMsg += "State: Waiting... (first call returns null)\n"; } LogStatus!(statusMsg); Sleep(5000); // Subscribe to the channel every 5 seconds } }c++ -
Cross-platform subscription example - Using UUID to subscribe to data sent from external systems
javascriptfunction main() { // Use a 32-bit UUID as the channel identifier let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" while (true) { // Subscribe to data on the 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 a 32-bit UUID as the channel identifier uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" while True: # Subscribe to data on the 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 secondsrustfn main() { // Rust's GetChannelData() function does not accept a channel ID parameter, so it cannot use a 32-bit UUID to subscribe to cross-platform data; // it can only read the latest data from the current live trading bot's own channel (i.e., data published by this bot via SetChannelData()) loop { // Subscribe to the channel's data let data = GetChannelData(); if !data.is_null() { Log!("Received cross-platform data:", data); } else { Log!("Waiting for data... (first call returns null)"); } Sleep(10000); // Check every 10 seconds } }c++
Returns
| Type | Description |
object / array / string / number / bool / null value | Returns the latest status data of the subscribed channel. It returns |
Arguments
| Name | Type | Required | Description |
channelId | string / number | Yes | The channel identifier, which supports the following two types:
|
See Also
Remarks
The GetChannelData() function is a non-blocking call. It returns immediately after being called and does not wait for data reception to complete.
The first time the GetChannelData() function is called, it returns null. You need to retry and wait for the channel data synchronization to complete.
Each call retrieves the latest status data on the channel, rather than a historical message queue.
A single live trading bot can subscribe to the channels of multiple different bots simultaneously; simply call GetChannelData() multiple times, passing in a different bot ID each time.
The current live trading bot can also subscribe to its own channel, meaning the robotId parameter can be the ID of the current bot.
Channel data can be transmitted across bots, across administrators, and across servers.
The broadcasting end uses the SetChannelData() function to publish channel data.
Channel communication is suitable for the live trading environment; this feature may be limited in the backtesting system.
The GetChannelData() function supports cross-platform subscription. When a 32-bit UUID is used as the channel identifier, it can receive data sent by external systems outside the FMZ platform via the HTTP API. The external system must specify both the bot ID and the UUID in order to send data; all live trading bots under the same administrator can subscribe to the data of that UUID channel, while bots under different administrators cannot subscribe.