Type/to search
Built-in Functions
Global
Version
Sleep
IsVirtual
Mail
Mail_Go
SetErrorFilter
GetPid
GetLastError
GetCommand
GetMeta
Dial
HttpQuery
HttpQuery_Go
Encode
UnixNano
Unix
GetOS
MD5
DBExec
UUID
EventLoop
__Serve
_G
_D
_N
_C
_Cross
JSON.parse
JSON.stringify
SetChannelData
GetChannelData
Log
Market
Trade
Account
Futures
NetSettings
Threads
threading
Thread
getThread
mainThread
currentThread
Lock
Condition
Event
Dict
pending
Thread
ThreadLock
ThreadEvent
ThreadCondition
ThreadDict
Web3
TA
Talib
talib.CDL2CROWS
talib.CDL3BLACKCROWS
talib.CDL3INSIDE
talib.CDL3LINESTRIKE
talib.CDL3OUTSIDE
talib.CDL3STARSINSOUTH
talib.CDL3WHITESOLDIERS
talib.CDLABANDONEDBABY
talib.CDLADVANCEBLOCK
talib.CDLBELTHOLD
talib.CDLBREAKAWAY
talib.CDLCLOSINGMARUBOZU
talib.CDLCONCEALBABYSWALL
talib.CDLCOUNTERATTACK
talib.CDLDARKCLOUDCOVER
talib.CDLDOJI
talib.CDLDOJISTAR
talib.CDLDRAGONFLYDOJI
talib.CDLENGULFING
talib.CDLEVENINGDOJISTAR
talib.CDLEVENINGSTAR
talib.CDLGAPSIDESIDEWHITE
talib.CDLGRAVESTONEDOJI
talib.CDLHAMMER
talib.CDLHANGINGMAN
talib.CDLHARAMI
talib.CDLHARAMICROSS
talib.CDLHIGHWAVE
talib.CDLHIKKAKE
talib.CDLHIKKAKEMOD
talib.CDLHOMINGPIGEON
talib.CDLIDENTICAL3CROWS
talib.CDLINNECK
talib.CDLINVERTEDHAMMER
talib.CDLKICKING
talib.CDLKICKINGBYLENGTH
talib.CDLLADDERBOTTOM
talib.CDLLONGLEGGEDDOJI
talib.CDLLONGLINE
talib.CDLMARUBOZU
talib.CDLMATCHINGLOW
talib.CDLMATHOLD
talib.CDLMORNINGDOJISTAR
talib.CDLMORNINGSTAR
talib.CDLONNECK
talib.CDLPIERCING
talib.CDLRICKSHAWMAN
talib.CDLRISEFALL3METHODS
talib.CDLSEPARATINGLINES
talib.CDLSHOOTINGSTAR
talib.CDLSHORTLINE
talib.CDLSPINNINGTOP
talib.CDLSTALLEDPATTERN
talib.CDLSTICKSANDWICH
talib.CDLTAKURI
talib.CDLTASUKIGAP
talib.CDLTHRUSTING
talib.CDLTRISTAR
talib.CDLUNIQUE3RIVER
talib.CDLUPSIDEGAP2CROWS
talib.CDLXSIDEGAP3METHODS
talib.AD
talib.ADOSC
talib.OBV
talib.ACOS
talib.ASIN
talib.ATAN
talib.CEIL
talib.COS
talib.COSH
talib.EXP
talib.FLOOR
talib.LN
talib.LOG10
talib.SIN
talib.SINH
talib.SQRT
talib.TAN
talib.TANH
talib.MAX
talib.MAXINDEX
talib.MIN
talib.MININDEX
talib.MINMAX
talib.MINMAXINDEX
talib.SUM
talib.HT_DCPERIOD
talib.HT_DCPHASE
talib.HT_PHASOR
talib.HT_SINE
talib.HT_TRENDMODE
talib.ATR
talib.NATR
talib.TRANGE
talib.BBANDS
talib.DEMA
talib.EMA
talib.HT_TRENDLINE
talib.KAMA
talib.MA
talib.MAMA
talib.MIDPOINT
talib.MIDPRICE
talib.SAR
talib.SAREXT
talib.SMA
talib.T3
talib.TEMA
talib.TRIMA
talib.WMA
talib.LINEARREG
talib.LINEARREG_ANGLE
talib.LINEARREG_INTERCEPT
talib.LINEARREG_SLOPE
talib.STDDEV
talib.TSF
talib.VAR
talib.ADX
talib.ADXR
talib.APO
talib.AROON
talib.AROONOSC
talib.BOP
talib.CCI
talib.CMO
talib.DX
talib.MACD
talib.MACDEXT
talib.MACDFIX
talib.MFI
talib.MINUS_DI
talib.MINUS_DM
talib.MOM
talib.PLUS_DI
talib.PLUS_DM
talib.PPO
talib.ROC
talib.ROCP
talib.ROCR
talib.ROCR100
talib.RSI
talib.STOCH
talib.STOCHF
talib.STOCHRSI
talib.TRIX
talib.ULTOSC
talib.WILLR
talib.AVGPRICE
talib.MEDPRICE
talib.TYPPRICE
talib.WCLPRICE
OS
Structures
Built-in Variables

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

TypeDescription

string

The current system version number, for example: 3.6.

Remarks

The system version number is the version number of the hosting program (docker/agent).

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

NameTypeRequiredDescription

millisecond

number

Yes

The millisecond parameter is used to set the sleep duration, in milliseconds.

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.

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

TypeDescription

bool

When the strategy runs in the backtesting system environment, it returns a truthy value, for example: true; when the strategy runs in the live trading environment, it returns a falsy value, for example: false.

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

TypeDescription

bool

Returns a truthy value, such as true, when the email is sent successfully; returns a falsy value, such as false, when sending fails.

Arguments

NameTypeRequiredDescription

smtpServer

string

Yes

Used to specify the SMTP server address of the email sender.

smtpUsername

string

Yes

Used to specify the email address of the email sender.

smtpPassword

string

Yes

Used to specify the SMTP service password of the email sender's mailbox.

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.

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 supported

Returns

TypeDescription

object

The Mail_Go function immediately returns a concurrent object. You can use the wait method of this concurrent object to get the email sending result. Returns a truthy value (e.g., true) if the email is sent successfully, and returns a falsy value (e.g., false) if sending fails.

Arguments

NameTypeRequiredDescription

smtpServer

string

Yes

Used to specify the SMTP server address of the email sender.

smtpUsername

string

Yes

Used to specify the email address of the email sender.

smtpPassword

string

Yes

The SMTP authorization password for the sender's email account.

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.

Filters error logs.

SetErrorFilter(filters)

Examples

  • Filter common errors.

    javascript
    function main() { SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused") }
    python
    def main(): SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
    rust
    fn 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.

    javascript
    function 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) }
    python
    def main(): order = exchange.GetOrder("123") Log(order) SetErrorFilter("502:|GetOrder") order = exchange.GetOrder("123") Log(order)
    rust
    fn 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

NameTypeRequiredDescription

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.

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

TypeDescription

string

Returns the ID of the live trading process.

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

TypeDescription

string

The most recent error message.

Remarks

This function does not work in the backtesting system.

Get the strategy's interactive command.

GetCommand()

Examples

  • Detect interactive commands, and when an interactive command is detected, use the Log function to output it.

    javascript
    function main(){ while(true) { var cmd = GetCommand() if (cmd) { Log(cmd) } Sleep(1000) } }
    python
    def main(): while True: cmd = GetCommand() if cmd: Log(cmd) Sleep(1000)
    rust
    fn 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 description Buy; this is a button control. Then add a control with an input box, name it sell, with the control description Sell; 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:

    javascript
    function 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) } }
    python
    def 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)
    rust
    fn 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

TypeDescription

string

The returned command format is ControlName:Data, where ControlName is the name of the control and Data is the data entered in the control. If the interactive control does not contain input components such as an input box or dropdown box (for example, a button control without an input box), then the returned command format is ControlName, i.e. only the control name is returned.

Remarks

This function is invalid in the backtesting system.

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

TypeDescription

string

Meta data.

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.

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:

    javascript
    function 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() } }
    python
    def 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()
    rust
    fn 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:

    javascript
    function 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() }
    python
    import 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()
    rust
    fn 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.

    javascript
    function 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&timestamp=${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) } }
    python
    import 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&timestamp={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)
    rust
    fn 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&timestamp={}", 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:

    javascript
    var 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") }
    python
    import 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")
    rust
    fn 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:

    javascript
    var 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()") }
    python
    import 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()")
    rust
    fn 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:

    javascript
    function 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) }
    python
    import 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)
    rust
    fn 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:

    javascript
    var 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
    // Omitted
    rust
    fn 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 Websocket interface of the MEXC exchange, subscribe to the public.aggre.deals.v3.api.pb channel, and use protobuf.js to decode the binary data:

    javascript
    let 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 the DBExec() 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 the close() function, you can still pass this handle into the Dial() function (for example, Dial(handle)) to reuse the connection.

    The following is an example of using the Dial function to connect to a sqlite3 database.

    javascript
    var 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 supported
    rust
    fn 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

TypeDescription

object

If the call times out, the Dial() function returns a null value; on a normal call, it returns a connection object. This connection object contains three methods: read, write, and close. Among them, the read method is used to read data, the write method is used to send data, and the close method is used to close the connection.The read method supports the following parameters:

  • When no parameter is passed, the function blocks and does not return until a message arrives. For example: ws.read().
  • When a parameter is passed, the unit is milliseconds, used to specify the timeout for waiting for a message. For example: ws.read(2000) means the timeout is two seconds (2000 milliseconds).
  • The following two parameters are valid only for WebSocket:
    Passing the parameter -1 means the function returns immediately regardless of whether there is a message. For example: ws.read(-1).
    Passing the parameter -2 means the function returns immediately regardless of whether there is a message, but only returns the latest message, and the remaining messages in the buffer are discarded. For example: ws.read(-2).read() function buffer notes:

For data pushed via the WebSocket protocol, if the time interval between two calls of the read() function in the strategy is too long, data may accumulate. This data is stored in the buffer, whose data structure is a queue with an upper limit of 2000 entries. When the amount of data exceeds 2000, the newest data enters the buffer and the oldest data is cleared.

ScenarioNo parameterParameter: -1Parameter: -2Parameter: 2000 (in milliseconds)
Buffer already has dataImmediately returns the oldest dataImmediately returns the oldest dataImmediately returns the newest dataImmediately returns the oldest data
Buffer has no dataBlocks until data is available, then returnsImmediately returns nullImmediately returns nullWaits 2000 milliseconds; returns null if there is no data, or returns the data if available
When the WebSocket connection is disconnected or the underlying layer reconnectsThe read() function returns an empty string (i.e. ""), and the write() function returns 0, which can be used to detect this situation. At this point you can use the close() function to close the connection; if automatic reconnection has been configured, there is no need to close it manually, as the system will reconnect automatically at the underlying level.
------

Arguments

NameTypeRequiredDescription

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 functionParameter description
Parameters related to WebSocket protocol data compression: compress=valuecompress 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=valuemode 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=trueUse enableCompression=false to disable this setting. It is disabled by default.
Parameters for configuring underlying auto-reconnection of the WebSocket protocol: reconnect=valuereconnect 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=valueinterval 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=valuepayload is the subscription message to be sent when the WebSocket reconnects, for example: payload=okok.
Parameters related to the socks5 proxy: proxy=valueproxy 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 databasesDriver projectConnection StringNotes
sqlite3github.com/mattn/go-sqlite3sqlite3://file:test.db?cache=shared&mode=memoryThe sqlite3:// prefix indicates that the sqlite3 database is used. Example call: Dial("sqlite3://test1.db")
mysqlgithub.com/go-sql-driver/mysqlmysql://username:yourpassword@tcp(localhost:3306)/yourdatabase?charset=utf8mb4--
postgresgithub.com/lib/pqpostgres://user=postgres dbname=yourdatabase sslmode=disable password=yourpassword host=localhost port=5432--
clickhousegithub.com/ClickHouse/clickhouse-goclickhouse://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&timestamp=" + 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

Sends an HTTP request.

HttpQuery(url)
HttpQuery(url, options)

Examples

  • An example of accessing the OKX public market data API interface.

    javascript
    function 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) }
    python
    import 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)
    rust
    fn 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.

    javascript
    function 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 library
    rust
    fn 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

TypeDescription

string / object

Returns the response data of the request. If the return value is a JSON string, it can be parsed using the JSON.parse() function in JavaScript strategies, and the json::parse() function in C++ strategies. In the options parameter structure, if debug is set to true, the return value is an object (JSON); if debug is set to false, the return value is a string.

Arguments

NameTypeRequiredDescription

url

string

Yes

The URL of the HTTP request.

options

object

No

Settings related to the HTTP request, for example the following structure:

json
{ method: "POST", body: "a=10&b=20&c=30", charset: "UTF-8", cookie: "session_id=12345; lang=en", debug: false, headers: {"TEST-HTTP-QUERY": "123"}, timeout: 1000 }
  • method: Used to set the request method.
  • body: Used to set the request body content, typically used for requests such as POST and PUT.
  • cookie: Used to set the Cookie in the request, typically used to carry authentication information or session identifiers.
  • headers: Used to set the request header information, which can be used to specify the content type, authentication information, etc.
  • debug: When set to true, this HttpQuery function call returns the complete response message; when set to false, it returns only the data in the response message Body.
  • timeout: Timeout setting; setting it to 1000 means the timeout is 1 second.
  • charset: Supports transcoding the response data of the request, for example: GB18030, supporting common encodings.

All fields in this structure are optional; for example, the headers field may be omitted.

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.

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

TypeDescription

object

The HttpQuery_Go() function immediately returns a concurrent object, and you can use the wait method of this concurrent object to get the result of the HTTP request. In JavaScript language strategies, you can use the JSON.parse() function to parse the returned data.

Arguments

NameTypeRequiredDescription

url

string

Yes

URL address for the HTTP request.

options

object

No

HTTP request configuration parameters, can use the following structure:

json
{ method: "POST", body: "a=10&b=20&c=30", charset: "UTF-8", cookie: "session_id=12345; lang=en", debug: false, headers: {"TEST-HTTP-QUERY": "123"}, timeout: 1000 }
  • debug: When set to true, this HttpQuery_Go function call returns the complete response message. When set to false, it only returns the data in the response message Body.
  • timeout: Timeout setting, setting 1000 means 1 second timeout.

All fields in this structure are optional, for example, you don't need to set the headers field.
The options parameter of the HttpQuery_Go function is consistent with the options parameter of the HttpQuery function and will not be repeated here.

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.

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.

    javascript
    function 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 }
    python
    def 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
    rust
    fn 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 algo also supports the following values: "text.encoder.utf8", "text.decoder.utf8", "text.encoder.gbk", "text.decoder.gbk", which are used to encode and decode strings.

    javascript
    function 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) }
    python
    def 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)
    rust
    fn 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

TypeDescription

string

The Encode function returns the encoded and encrypted data.

Arguments

NameTypeRequiredDescription

algo

string

Yes

The algo parameter is used to specify the algorithm used in the encoding computation. It supports being set to one of the following values: "raw" (no algorithm used), "sign", "signTx", "md4", "md5", "sha256", "sha512", "sha1", "keccak256", "sha3.224", "sha3.256", "sha3.384", "sha3.512", "sha3.keccak256", "sha3.keccak512", "sha512.384", "sha512.256", "sha512.224", "ripemd160", "blake2b.256", "blake2b.512", "blake2s.128", "blake2s.256".

The algo parameter also supports "text.encoder.utf8", "text.decoder.utf8", "text.encoder.gbk", "text.decoder.gbk", which are used to encode or decode strings.

The algo parameter also supports the "ed25519" algorithm, which can be used in combination with different hash algorithms. For example, the algo parameter can be written as "ed25519.md5", "ed25519.sha512", etc., and ed25519.seed computation is also supported.

inputFormat

string

Yes

Used to specify the data format of the data parameter. The inputFormat parameter supports being set to one of "raw", "hex", "base64", "string". "raw" indicates raw data, "hex" indicates hex-encoded data, "base64" indicates base64-encoded data, and "string" indicates string data.

outputFormat

string

Yes

Used to specify the output data format. The outputFormat parameter supports being set to one of "raw", "hex", "base64", "string". "raw" indicates raw data, "hex" indicates hex-encoded data, "base64" indicates base64-encoded data, and "string" indicates string data.

data

string

Yes

The data parameter is the data to be processed.

keyFormat

string

No

Used to specify the data format of the key parameter. The keyFormat parameter supports being set to one of "raw", "hex", "base64", "string". "raw" indicates raw data, "hex" indicates hex-encoded data, "base64" indicates base64-encoded data, and "string" indicates string data.

key

string

No

The key parameter is the key used for HMAC encryption.

When the algo parameter is set to "sign" or "signTx", the key parameter must be passed in.

When the algo parameter is set to "raw", the key parameter is not used for HMAC encryption (because HMAC encryption must specify an algorithm).

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.

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

TypeDescription

number

The UnixNano() function returns a nanosecond-level timestamp.

See Also

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

TypeDescription

number

Returns the second-level timestamp.

See Also

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

TypeDescription

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.

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

TypeDescription

string

The MD5 hash value.

Arguments

NameTypeRequiredDescription

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.

Database interface function.

DBExec(sql)

Examples

  • Supports in-memory databases. For the parameter of the DBExec function, 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:

    javascript
    function 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;")) }
    python
    def 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;"))
    rust
    fn 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.

    javascript
    function 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) }
    python
    def 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)
    rust
    fn 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.

    javascript
    function 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)) }
    python
    def 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))
    rust
    fn 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

TypeDescription

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

NameTypeRequiredDescription

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.

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

TypeDescription

string

A 32-bit UUID.

Remarks

The UUID() function is only supported in live trading.

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

TypeDescription

object

If the returned object is not empty, the Event field in the returned content indicates the trigger type of the event. For example, the following return value structure:

json
{"Seq":1,"Event":"Exchange_GetTrades","ThreadId":0,"Index":3,"Nano":1682068771309583400}

Arguments

NameTypeRequiredDescription

timeout

number

No

The timeout parameter is used to set the timeout period, in milliseconds.

When timeout is set to 0, the function will wait indefinitely and return only when an event occurs; when timeout is greater than 0, it specifies the timeout period for waiting on events; when timeout is less than 0, it returns the most recent event immediately.

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.

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 supported

Returns

TypeDescription

string

Returns a string recording the IP address and port of the created service. For example: 127.0.0.1:8088, [::]:8089.

Arguments

NameTypeRequiredDescription

serveURI

string

Yes

The serveURI parameter is used to configure the protocol, IP address, port and other settings for service binding, for example: http://0.0.0.0:8088?gzip=true, i.e.: http://:8088?gzip=true.

  • TCP Protocol
    serveURI parameter setting example: tcp://127.0.0.1:6666?tls=true; certificates and private keys can be added, for example: tls=true&cert_pem=xxxx&cert_key_pem=xxxx.
  • HTTP Protocol
    serveURI parameter setting example: http://127.0.0.1:6666?gzip=true; compression settings can be configured: gzip=true.
    serveURI parameter for HTTPS example: https://127.0.0.1:6666?tls=true&gzip=true; cert_pem and cert_key_pem parameters can be added to load certificates.

handler

function

Yes

The handler parameter is used to pass in the route handler function (HTTP protocol), message handler function (TCP protocol), or Stream handler function (WebSocket).

The callback function passed in via the handler parameter can define multiple parameters, with the first parameter being the ctx object (context object).

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 handler parameter, there may be multiple arg parameters, for example:

javascript
__Serve("http://:8088", function(ctx, a, b, c) { Log(`ctx.host():`, ctx.host(), ", a=", a, ", b=", b, ", c=", c) }, 1, 2, 3)

The parameters 1, 2, 3 passed when calling the __Serve() function correspond to the parameters a, b, c of the callback function.

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.

    • WebSocket service is implemented based on HTTP protocol. You can set a routing branch in the path and design the implementation code for WebSocket message 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, calling ctx.query("num") in the callback handler function passed in the handler parameter 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 the User-Agent in the headers of the current request: ctx.header("User-Agent").
  • ctx.method()
    Applies to HTTP protocol, returns the request method when called, such as GET, 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 the ctx context 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. The read method is not supported in regular HTTP protocol; you can specify the timeout parameter timeout_ms in milliseconds.
  • ctx.write(s)
    Applies to HTTP/TCP protocol, used to write string data. You can use JSON.stringify() to encode JSON objects as strings before writing. For WebSocket protocol, this method can be used to pass the encoded string to the client.

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

TypeDescription

string / number / bool / object / array / null

The value data in the persistently stored k-v key-value pair.

Arguments

NameTypeRequiredDescription

k

string / null

No

The parameter k is the key name in the key-value pair to be stored; it is case-insensitive.

v

string / number / bool / object / array / null

No

The parameter v is the value in the key-value pair to be stored; it can be any data that can be JSON serialized.

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.

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:

    javascript
    function main(){ var time = _D() Log(time) }
    python
    def main(): strTime = _D() Log(strTime)
    rust
    fn main() { let time = _D(None); Log!(time); }
    c++
    void main() { auto strTime = _D(); Log(strTime); }
  • The timestamp is 1574993606000; convert it with code:

    javascript
    function main() { Log(_D(1574993606000)) }
    python
    def 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))
    rust
    fn main() { Log!(_D(1574993606000)); }
    c++
    void main() { Log(_D(1574993606000)); }
  • Format using the fmt argument. The format strings for JavaScript, Python, and C++ differ; please refer to the following examples for details:

    javascript
    function main() { Log(_D(1574993606000, "yyyy--MM--dd hh--mm--ss")) // 2019--11--29 10--13--26 }
    python
    def main(): # 1574993606 is a second-level timestamp Log(_D(1574993606, "%Y--%m--%d %H--%M--%S")) # 2019--11--29 10--13--26
    rust
    fn 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

TypeDescription

string

The time string.

Arguments

NameTypeRequiredDescription

timestamp

number / object

No

A millisecond-level timestamp or a Date object.

fmt

string

No

The format string. Default format for JavaScript: yyyy-MM-dd hh:mm:ss; default format for Python: %Y-%m-%d %H:%M:%S; default format for C++: %Y-%m-%d %H:%M:%S.

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.

Format a floating-point number.

_N()
_N(num)
_N(num, precision)

Examples

  • For example, _N(3.1415, 2) keeps 3.1415 to two decimal places, removes the remaining digits, and the function returns 3.14.

    javascript
    function main(){ var i = 3.1415 Log(i) var ii = _N(i, 2) Log(ii) }
    python
    def main(): i = 3.1415 Log(i) ii = _N(i, 2) Log(ii)
    rust
    fn 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:

    javascript
    function main(){ var i = 1300 Log(i) var ii = _N(i, -3) // Check the log and you will see it is 1000 Log(ii) }
    python
    def main(): i = 1300 Log(i) ii = _N(i, -3) Log(ii)
    rust
    fn 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

TypeDescription

number

The floating-point number formatted according to the precision setting.

Arguments

NameTypeRequiredDescription

num

number

Yes

The floating-point number to be formatted.

precision

number

No

Used to set the formatting precision. The parameter precision is an integer, with a default value of 4.

See Also

Remarks

The parameter precision can be a positive integer or a negative integer.

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:

    javascript
    function 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) }
    python
    def main(): ticker = _C(exchange.GetTicker) _CDelay(2000) depth = _C(exchange.GetDepth) Log(ticker) Log(depth)
    rust
    fn 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:

    javascript
    function main(){ var records = _C(exchange.GetRecords, PERIOD_D1) Log(records) }
    python
    def main(): records = _C(exchange.GetRecords, PERIOD_D1) Log(records)
    rust
    fn 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:

    javascript
    var 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) }
    python
    import 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)
    rust
    fn 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

TypeDescription

All types supported by the platform except false values and null values (any).

The return value after the callback function is executed.

Arguments

NameTypeRequiredDescription

pfn

function

Yes

The parameter pfn is a function reference, i.e. a callback function.

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 arg parameters. The type and number of the arg parameters are determined by the parameters of the callback function.

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()).

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

TypeDescription

number

The number of crossover periods between array arr1 and array arr2.

Arguments

NameTypeRequiredDescription

arr1

array

Yes

An array whose elements are of type number.

arr2

array

Yes

An array whose elements are of type number.

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.

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

TypeDescription

object

The return value is a JSON object.

Arguments

NameTypeRequiredDescription

s

string

Yes

This parameter is the JSON string that needs to be decoded (parsed).

safeStr

bool

No

When this parameter is set to true, if a value that may exceed the precision range is encountered during parsing, it will be returned as a string to avoid precision loss or numeric overflow issues.

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.

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++
// Omitted

Returns

TypeDescription

string

Returns the serialized JSON string.

Arguments

NameTypeRequiredDescription

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.

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

    javascript
    function 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 } }
    python
    def 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 minute
    rust
    fn 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 seconds
    rust
    // 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

TypeDescription

null

This function has no return value.

Arguments

NameTypeRequiredDescription

data

object / array / string / number / bool / null

Yes

The data to be published to the channel. It can be any data structure that supports JSON serialization, and is typically an object containing the live trading bot's status information.

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.

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

    javascript
    function 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 } }
    python
    def 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 seconds
    rust
    fn 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

    javascript
    function 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 } }
    python
    def 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 seconds
    rust
    fn 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

TypeDescription

object / array / string / number / bool / null value

Returns the latest status data of the subscribed channel. It returns null on the first call, in which case a retry is required. The data structure is determined by the data published by the broadcasting end.

Arguments

NameTypeRequiredDescription

channelId

string / number

Yes

The channel identifier, which supports the following two types:

  1. Bot ID: Used to subscribe to the channel data of other live trading bots (i.e., inter-bot communication). The bot ID can be obtained via the _G() function.

  2. 32-bit UUID: Used to subscribe to data sent across platforms (i.e., data sent to the FMZ platform by external systems via the HTTP API).

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.