Strategy Framework and API Functions
In strategies written in JavaScript, Python, Rust, or C++, you need to call the Sleep() function within the strategy's main loop. During backtesting, it is used to control the backtesting speed; in live trading, it is used to control the strategy's polling interval, thereby controlling the request frequency to the exchange's API interface.
Examples
-
加密货币策略基本框架范例:
javascriptfunction onTick(){ //在这里写策略逻辑,将会不断调用,例如打印行情信息 Log(exchange.GetTicker()) } function main(){ while(true){ onTick() // Sleep函数主要用于数字货币策略的轮询频率控制,防止访问交易所API接口过于频繁 Sleep(60000) } }pythondef onTick(): Log(exchange.GetTicker()) def main(): while True: onTick() Sleep(60000)rustfn onTick() { // 在这里写策略逻辑,将会不断调用,例如打印行情信息 Log!(exchange.GetTicker(None)); } fn main() { loop { onTick(); // Sleep函数主要用于数字货币策略的轮询频率控制,防止访问交易所API接口过于频繁 Sleep(60000); } }c++void onTick() { Log(exchange.GetTicker()); } void main() { while(true) { onTick(); Sleep(60000); } } -
举个最简单的例子,如果我想每隔1秒种就在交易所挂一个价格为100,数量为1的买单可以这样写:
javascriptfunction onTick(){ // 这个仅仅是例子,回测或者实盘会很快把资金全部用于下单,实盘请勿使用 exchange.Buy(100, 1) } function main(){ while(true){ onTick() // 暂停多久可自定义,单位为毫秒,1秒等于1000毫秒 Sleep(1000) } }pythondef onTick(): exchange.Buy(100, 1) def main(): while True: onTick() Sleep(1000)rustfn onTick() { // 这个仅仅是例子,回测或者实盘会很快把资金全部用于下单,实盘请勿使用 let _ = exchange.Buy(100, 1); } fn main() { loop { onTick(); // 暂停多久可自定义,单位为毫秒,1秒等于1000毫秒 Sleep(1000); } }c++void onTick() { exchange.Buy(100, 1); } void main() { while(true) { onTick(); Sleep(1000); } } -
设计一个On Bar架构的策略
javascriptfunction onTick() { Log("K-line updated, new BAR generated") } function main() { var exName = exchange.GetName() if (exName.includes("Futures_")) { exchange.SetContractType("swap") } var lastTs = 0 while (true) { var r = _C(exchange.GetRecords) if (r.length > 0 && r[r.length - 1].Time != lastTs) { onTick() lastTs = r[r.length - 1].Time } Sleep(1000) } }pythondef onTick(): Log("K-line updated, new BAR generated") def main(): exName = exchange.GetName() if "Futures_" in exName: exchange.SetContractType("swap") lastTs = 0 while True: r = _C(exchange.GetRecords) if len(r) > 0 and r[-1]["Time"] != lastTs: onTick() lastTs = r[-1].Time Sleep(1000)rustfn onTick() { Log!("K-line updated, new BAR generated"); } fn main() { let exName = exchange.GetName(); if exName.contains("Futures_") { let _ = exchange.SetContractType("swap"); } let mut lastTs = 0; loop { let r = _C!(exchange.GetRecords(None, None, None)); if r.len() > 0 && r[r.len() - 1].Time != lastTs { onTick(); lastTs = r[r.len() - 1].Time; } Sleep(1000); } }c++void onTick() { Log("K-line updated, new BAR generated"); } void main() { auto exName = exchange.GetName(); if (exName.find("Futures_") != std::string::npos) { exchange.SetContractType("swap"); } Record lastBar; lastBar.Time = 0; while (true) { auto r = _C(exchange.GetRecords); if (r.size() > 0 && r[r.size() - 1].Time != lastBar.Time) { onTick(); lastBar.Time = r[r.size() - 1].Time; } Sleep(1000); } } -
以下展示所有API接口的速查表,详细的API描述请参考:发明者量化交易平台API手册。
Global Functions
| Function Name | Description |
|---|---|
| Version | Returns the current system version number |
| Sleep | Sleep function, parameter is the number of milliseconds to pause |
| IsVirtual | Determines the execution environment, returns true for backtesting environment |
| Send email | |
| Mail_Go | Asynchronous version of the Mail function |
| SetErrorFilter | Filter error logs, parameter is a regular expression string, error logs matching this regex will not be uploaded to the log system |
| GetPid | Get live trading process ID |
| GetLastError | Get the most recent error message |
| GetCommand | Get strategy interaction commands, for strategy interaction control settings please refer to: Interactive Controls |
| GetMeta | Get the Meta value written when generating the strategy registration code |
| Dial | Used for raw Socket access |
| HttpQuery | Send HTTP request |
| HttpQuery_Go | Asynchronous version of the HttpQuery function |
| Encode | Data encoding function |
| UnixNano | Get nanosecond timestamp |
| Unix | Get second-level timestamp |
| GetOS | Get system information |
| MD5 | Calculate MD5 hash value |
| DBExec | Database function for executing SQL statements and performing database operations |
| UUID | Generate UUID |
| EventLoop | Listen for events, returns when any WebSocket is readable or concurrent tasks like exchange.Go, HttpQuery_Go are completed, this function is only available for live trading |
| _G | Persistently save data, this function implements a saveable global dictionary feature. The data structure is a key-value pair table, permanently saved in the docker's local database file |
| _D | Timestamp processing function, converts millisecond timestamp or Date object to time string |
| _N | Format floating-point numbers, for example _N(3.1415, 2) will remove digits after the second decimal place of 3.1415, the function returns 3.14 |
| _C | Retry function for interface fault tolerance. Note that for fault tolerance of exchange.GetTicker function, use _C(exchange.GetTicker) instead of _C(exchange.GetTicker()) |
| _Cross | Crossover detection function, _Cross() returns a positive number indicating the number of periods since upward crossover, negative number for downward crossover, 0 means current prices are equal |
| JSONParse | Parse JSON, can correctly parse JSON strings containing large numeric values, parsing large numbers as string type. The backtesting system does not support the JSONParse() function |
| SetChannelData | Publish latest status data on a channel for inter-bot communication |
| GetChannelData | Subscribe to channel data from specified live trading bot for inter-bot communication |
Logging Functions
| Function Name | Description |
|---|---|
| Log | Output logs, supports setting log text color, push notifications, and printing base64-encoded images |
| LogProfit | Output profit/loss data, print P&L values and draw profit curves based on the values |
| LogProfitReset | Clear all profit logs and profit charts output by the LogProfit function |
| LogStatus | Output information in the status bar, supports setting button controls and outputting tables in the status bar |
| EnableLog | Enable or disable logging for order information |
| Chart | Chart drawing function, based on Highcharts/Highstocks chart library |
| KLineChart | Pine language-style chart drawing function, used for custom drawing in a Pine-like manner during strategy execution |
| LogReset | Clear logs, supports retaining a specified number of recent log records through parameters |
| LogVacuum | Reclaim SQLite resources, reclaim storage space occupied by SQLite when deleting data after calling LogReset() function to clear logs |
| console.log | Output debug information in the "Debug Info" section of the live trading page |
| console.error | Output error information in the "Debug Info" section of the live trading page |
Market Functions
| Function Name | Description |
|---|---|
| exchange.GetTicker | Get tick market data |
| exchange.GetDepth | Get order book depth data |
| exchange.GetTrades | Get market trade records |
| exchange.GetRecords | Get K-line data |
| exchange.GetPeriod | Get current K-line period |
| exchange.SetMaxBarLen | Set maximum K-line length |
| exchange.GetRawJSON | Get raw content returned from the most recent REST request |
| exchange.GetRate | Get current exchange rate value |
| exchange.SetData | Set data loaded at strategy runtime |
| exchange.GetData | Get loaded data or data provided by external links |
| exchange.GetMarkets | Get exchange market information |
| exchange.GetTickers | Get exchange aggregated market data |
Trading Functions
| Function Name | Description |
|---|---|
| exchange.Buy | Submit a buy order. When placing futures contract orders, ensure the trading direction is set correctly; an error will occur if the trading direction does not match the trading function |
| exchange.Sell | Submit a sell order. When placing futures contract orders, ensure the trading direction is set correctly; an error will occur if the trading direction does not match the trading function |
| exchange.CreateOrder | Submit an order by specifying the trading instrument, trading direction, price, and quantity through parameters |
| exchange.ModifyOrder | Modify the price and quantity of a regular order, supports modifying other order attributes through additional parameters |
| exchange.ModifyConditionOrder | Modify the quantity and trigger conditions of a conditional order, supports modifying other conditional order attributes through additional parameters |
| exchange.CancelOrder | Cancel an order |
| exchange.GetOrder | Get order information, data structure is Order structure |
| exchange.GetOrders | Get unfilled orders, data structure is an array (list) of Order structures |
| exchange.GetHistoryOrders | Get historical orders for the current trading pair/contract, supports specifying specific trading instruments |
| exchange.SetPrecision | Set the price and order quantity precision for the exchange object, the system will automatically truncate excess digits after setting |
| exchange.SetRate | Set the exchange rate |
| exchange.IO | Used for other interface calls related to the exchange object |
| exchange.Log | Output and record trading logs without actually placing orders |
| exchange.Encode | Signature encryption calculation |
| exchange.Go | Multi-threaded asynchronous support function |
| exchange.GetAccount | Get account information |
| exchange.GetAssets | Request exchange account asset information |
| exchange.GetName | Get the name of the exchange object |
| exchange.GetLabel | Get the label of the exchange object |
| exchange.GetCurrency | Get the current trading pair |
| exchange.SetCurrency | Switch trading pair |
| exchange.GetQuoteCurrency | Get the quote currency name of the current trading pair |
Futures Functions
| Function Name | Description |
|---|---|
| exchange.GetPositions | Get futures position information, returns an array (list) of Position structures |
| exchange.SetMarginLevel | Set leverage multiplier |
| exchange.SetDirection | Set the order direction for exchange.Buy and exchange.Sell functions when placing orders in futures contracts |
| exchange.SetContractType | Set contract code, for example: exchange.SetContractType("swap") sets the contract code to swap, setting the current operating contract to perpetual contract |
| exchange.GetContractType | Get the currently set contract code |
| exchange.GetFundings | Get funding rate data for perpetual contracts on the current futures exchange |
Network Functions
| Function Name | Description |
|---|---|
| exchange.SetBase | Set the base address of the exchange API interface |
| exchange.GetBase | Get the current base address of the exchange API interface |
| exchange.SetProxy | Set network proxy |
| exchange.SetTimeout | Set timeout for REST protocol |
API Rate Limiting Control
Overview
The API rate limiting control feature is used to limit how frequently a strategy calls the exchange's API, preventing account bans or temporary restrictions caused by triggering the exchange's rate limits. The FMZ platform provides flexible rate limiting configuration options, supporting two rate limiting modes and multiple configuration strategies.
Why API Rate Limiting Is Needed
-
Avoid triggering exchange limits: Most exchanges impose strict limits on API call frequency; once exceeded, your account may be temporarily or permanently banned.
-
Allocate API quota sensibly: In multi-strategy, multi-trading-pair scenarios, API call resources need to be allocated sensibly.
-
Improve strategy stability: By proactively rate limiting, you avoid connection failures and data retrieval anomalies caused by frequent calls.
-
Comply with exchange rules: Adhere to the exchange's API usage rules and maintain a healthy API usage relationship.
Two Rate Limiting Modes
rate mode (smooth rate limiting)
-
Suitable for general rate limiting needs
-
Does not strictly align to time windows
-
Distributes calls relatively smoothly
-
Recommended for everyday API call limiting
quota mode (quota-based rate limiting)
-
Strictly aligns to time windows
-
For example: when set to
"1s", the window aligns to whole seconds; when set to"1m", the window aligns to whole minutes -
Suitable for scenarios that require strict time window control
-
Recommended for intraday quota management
Basic Usage
Basic rate Mode Example
Examples
- undefinedjavascriptfunction main() { // Limit GetTicker to maximum 10 times per second exchange.IO("rate", "GetTicker", 10, "1s") // Normal API calls for (var i = 0; i < 20; i++) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { Log("Success:", ticker.Last) } else { Log("Rate limit exceeded") // Returns null when exceeding 10 times/second } Sleep(50) } }pythondef main(): # Limit GetTicker to maximum 10 times per second exchange.IO("rate", "GetTicker", 10, "1s") # Normal API calls for i in range(20): ticker = exchange.GetTicker("BTC_USDT") if ticker: Log("Success:", ticker["Last"]) else: Log("Rate limit exceeded") # Returns None when exceeding 10 times/second Sleep(50)rustfn main() { // Limit GetTicker to maximum 10 times per second let _ = exchange.IO(("rate", "GetTicker", 10, "1s")); // Normal API calls for _i in 0..20 { match exchange.GetTicker("BTC_USDT") { Ok(ticker) => Log!("Success:", ticker.Last), Err(_) => Log!("Rate limit exceeded"), // Returns Err when exceeding 10 times/second } Sleep(50); } }c++// C++ is not supported yet
-
Basic quota Mode Example
javascriptfunction main() { // Strict limit, time window aligned to whole seconds exchange.IO("quota", "GetTicker", 5, "1s") for (var i = 0; i < 10; i++) { var ticker = exchange.GetTicker("BTC_USDT") Log(_D(), "Call", i+1, ticker ? "Success" : "Quota exceeded") Sleep(150) // About 6-7 calls per second, will trigger limit } }pythondef main(): # Strict limit, time window aligned to whole seconds exchange.IO("quota", "GetTicker", 5, "1s") for i in range(10): ticker = exchange.GetTicker("BTC_USDT") Log(_D(), "Call", i+1, "Success" if ticker else "Quota exceeded") Sleep(150) # About 6-7 calls per second, will trigger limitrustfn main() { // Strict limit, time window aligned to whole seconds let _ = exchange.IO(("quota", "GetTicker", 5, "1s")); for i in 0..10 { match exchange.GetTicker("BTC_USDT") { Ok(_) => Log!(_D(None), "Call", i + 1, "Success"), Err(_) => Log!(_D(None), "Call", i + 1, "Quota exceeded"), } Sleep(150); // About 6-7 calls per second, will trigger limit } }c++// C++ is not supported yet -
Function Name Configuration
Rate Limiting a Single Function
javascriptfunction main() { // Only limit GetTicker function exchange.IO("rate", "GetTicker", 10, "1s") // GetTicker is limited, GetDepth is not limited exchange.GetTicker("BTC_USDT") exchange.GetDepth("BTC_USDT") }pythondef main(): # Only limit GetTicker function exchange.IO("rate", "GetTicker", 10, "1s") # GetTicker is limited, GetDepth is not limited exchange.GetTicker("BTC_USDT") exchange.GetDepth("BTC_USDT")rustfn main() { // Only limit GetTicker function let _ = exchange.IO(("rate", "GetTicker", 10, "1s")); // GetTicker is limited, GetDepth is not limited let _ = exchange.GetTicker("BTC_USDT"); let _ = exchange.GetDepth("BTC_USDT"); }c++// C++ not supported yet -
Joint Rate Limiting Across Multiple Functions
javascriptfunction main() { // GetTicker and GetDepth share quota, total 10 times per second exchange.IO("rate", "GetTicker,GetDepth", 10, "1s") for (var i = 0; i < 15; i++) { if (i % 2 == 0) { exchange.GetTicker("BTC_USDT") // Counted in shared quota } else { exchange.GetDepth("BTC_USDT") // Counted in shared quota } } }pythondef main(): # GetTicker and GetDepth share quota, total 10 times per second exchange.IO("rate", "GetTicker,GetDepth", 10, "1s") for i in range(15): if i % 2 == 0: exchange.GetTicker("BTC_USDT") # Counted in shared quota else: exchange.GetDepth("BTC_USDT") # Counted in shared quotarustfn main() { // GetTicker and GetDepth share quota, total 10 times per second let _ = exchange.IO(("rate", "GetTicker,GetDepth", 10, "1s")); for i in 0..15 { if i % 2 == 0 { let _ = exchange.GetTicker("BTC_USDT"); // Counted in shared quota } else { let _ = exchange.GetDepth("BTC_USDT"); // Counted in shared quota } } }c++// C++ not supported yet -
Restrict All Functions Using Wildcards
javascriptfunction main() { // Limit all API calls to total 100 times per minute exchange.IO("rate", "*", 100, "1m") // All calls are counted in total quota exchange.GetTicker("BTC_USDT") exchange.GetDepth("BTC_USDT") exchange.GetAccount() exchange.CreateOrder("BTC_USDT", "buy", 50000, 0.001) }pythondef main(): # Limit all API calls to total 100 times per minute exchange.IO("rate", "*", 100, "1m") # All calls are counted in total quota exchange.GetTicker("BTC_USDT") exchange.GetDepth("BTC_USDT") exchange.GetAccount() exchange.CreateOrder("BTC_USDT", "buy", 50000, 0.001)rustfn main() { // Limit all API calls to total 100 times per minute let _ = exchange.IO(("rate", "*", 100, "1m")); // All calls are counted in total quota let _ = exchange.GetTicker("BTC_USDT"); let _ = exchange.GetDepth("BTC_USDT"); let _ = exchange.GetAccount(); let _ = exchange.CreateOrder("BTC_USDT", "buy", 50000, 0.001); }c++// C++ not supported yet -
Time Period Configuration
Supported Time Units
-
ns: nanoseconds -
usorµs: microseconds -
ms: milliseconds -
s: seconds -
m: minutes -
h: hours -
d: days
Example:
"100ms","1s","5m","1h","1d"javascriptfunction main() { // Different time period configurations exchange.IO("rate", "GetTicker", 10, "1s") // 10 times per second exchange.IO("rate", "GetDepth", 30, "1m") // 30 times per minute exchange.IO("rate", "GetAccount", 100, "1h") // 100 times per hour exchange.IO("rate", "CreateOrder", 500, "1d") // 500 times per day }pythondef main(): # Configurations for different time periods exchange.IO("rate", "GetTicker", 10, "1s") # 10 times per second exchange.IO("rate", "GetDepth", 30, "1m") # 30 times per minute exchange.IO("rate", "GetAccount", 100, "1h") # 100 times per hour exchange.IO("rate", "CreateOrder", 500, "1d") # 500 times per dayrustfn main() { // Configurations for different time periods let _ = exchange.IO(("rate", "GetTicker", 10, "1s")); // 10 times per second let _ = exchange.IO(("rate", "GetDepth", 30, "1m")); // 30 times per minute let _ = exchange.IO(("rate", "GetAccount", 100, "1h")); // 100 times per hour let _ = exchange.IO(("rate", "CreateOrder", 500, "1d")); // 500 times per day }c++// C++ not supported yet -
-
Reset Time Point Configuration
Use
@HHMMor@HHMMSSformat to specify the daily reset time point, valid only in quota mode.javascriptfunction main() { // Reset quota daily at 08:15 exchange.IO("quota", "GetTicker", 1000, "@0815") // Reset quota daily at 00:00 exchange.IO("quota", "CreateOrder", 500, "@0000") // Reset quota daily at 23:59:59 exchange.IO("quota", "*", 5000, "@235959") }pythondef main(): # Reset quota daily at 08:15 exchange.IO("quota", "GetTicker", 1000, "@0815") # Reset quota daily at 00:00 exchange.IO("quota", "CreateOrder", 500, "@0000") # Reset quota daily at 23:59:59 exchange.IO("quota", "*", 5000, "@235959")rustfn main() { // Reset quota daily at 08:15 let _ = exchange.IO(("quota", "GetTicker", 1000, "@0815")); // Reset quota daily at 00:00 let _ = exchange.IO(("quota", "CreateOrder", 500, "@0000")); // Reset quota daily at 23:59:59 let _ = exchange.IO(("quota", "*", 5000, "@235959")); }c++// C++ not supported yet -
Behavior Modes
Default Mode (returns null when limit exceeded)
javascriptfunction main() { exchange.IO("rate", "GetTicker", 5, "1s") // behavior parameter not specified for (var i = 0; i < 10; i++) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { Log("Call", i+1, "Success:", ticker.Last) } else { Log("Call", i+1, "Failed: rate limit exceeded") // Optionally Sleep to wait, or skip this call Sleep(200) } } }pythondef main(): exchange.IO("rate", "GetTicker", 5, "1s") # behavior parameter not specified for i in range(10): ticker = exchange.GetTicker("BTC_USDT") if ticker: Log("Call", i+1, "Success:", ticker["Last"]) else: Log("Call", i+1, "Failed: rate limit exceeded") # Optionally Sleep to wait, or skip this call Sleep(200)rustfn main() { let _ = exchange.IO(("rate", "GetTicker", 5, "1s")); // behavior parameter not specified for i in 0..10 { match exchange.GetTicker("BTC_USDT") { Ok(ticker) => Log!("Call", i + 1, "Success:", ticker.Last), Err(_) => { Log!("Call", i + 1, "Failed: rate limit exceeded"); // Optionally Sleep to wait, or skip this call Sleep(200); } } } }c++// C++ not supported yet -
delay mode (automatically wait when rate limit is exceeded)
javascriptfunction main() { exchange.IO("rate", "GetTicker", 5, "1s", "delay") // Specify the delay parameter // When the call exceeds the rate limit, it automatically waits to ensure every call succeeds for (var i = 0; i < 10; i++) { var ticker = exchange.GetTicker("BTC_USDT") Log("Call", i+1, "Success:", ticker.Last) // ticker will not be null } }pythondef main(): exchange.IO("rate", "GetTicker", 5, "1s", "delay") # Specify the delay parameter # When the call exceeds the rate limit, it automatically waits to ensure every call succeeds for i in range(10): ticker = exchange.GetTicker("BTC_USDT") Log("Call", i+1, "Success:", ticker["Last"]) # ticker will not be Nonerustfn main() { let _ = exchange.IO(("rate", "GetTicker", 5, "1s", "delay")); // Specify the delay parameter // When the call exceeds the rate limit, it automatically waits to ensure every call succeeds for i in 0..10 { let ticker = exchange.GetTicker("BTC_USDT").unwrap(); Log!("Call", i + 1, "Success:", ticker.Last); // ticker will not return Err } }c++// Not yet supported in C++ -
List of Supported Functions
Trading Functions
CreateOrder: Create an orderCancelOrder: Cancel an orderBuy: Buy (subject to CreateOrder restrictions)Sell: Sell (subject to CreateOrder restrictions)CreateConditionOrder: Create a conditional orderCancelConditionOrder: Cancel a conditional order
Account Functions
GetAccount: Get account informationGetAssets: Get asset informationGetPositions: Get position information
Order Functions
GetOrder: Get a single orderGetOrders: Get all ordersGetHistoryOrders: Get historical ordersGetConditionOrder: Get a single conditional orderGetConditionOrders: Get all conditional ordersGetHistoryConditionOrders: Get historical conditional orders
Market Data Functions
GetTicker: Get a single tickerGetTickers: Get multiple tickersGetDepth: Get market depthGetRecords: Get K-line (candlestick) dataGetTrades: Get the latest trade records
Other Functions
GetMarkets: Get the list of marketsGetFundings: Get funding ratesSetMarginLevel: Set the leverage levelGo: Concurrent call (subject to the restrictions of the actual function being called)IO/api: Custom API call (limited to exchange.IO("api", ...))
-
Practical Application Scenarios
### Scenario 1: Preventing Exchange Rate Limit Triggers
javascriptfunction main() { // Assume exchange limits: GetTicker 20 times per second, CreateOrder 5 times per second // Set the rate slightly below the exchange limit to reserve a safety margin exchange.IO("rate", "GetTicker", 15, "1s") exchange.IO("rate", "CreateOrder", 4, "1s") while (true) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker && ticker.Last < 50000) { exchange.CreateOrder("BTC_USDT", "buy", ticker.Last, 0.001) } Sleep(100) } }pythondef main(): # Assume exchange limits: GetTicker 20 times per second, CreateOrder 5 times per second # Set the rate slightly below the exchange limit to reserve a safety margin exchange.IO("rate", "GetTicker", 15, "1s") exchange.IO("rate", "CreateOrder", 4, "1s") while True: ticker = exchange.GetTicker("BTC_USDT") if ticker and ticker["Last"] < 50000: exchange.CreateOrder("BTC_USDT", "buy", ticker["Last"], 0.001) Sleep(100)rustfn main() { // Assume exchange limits: GetTicker 20 times per second, CreateOrder 5 times per second // Set the rate slightly below the exchange limit to reserve a safety margin let _ = exchange.IO(("rate", "GetTicker", 15, "1s")); let _ = exchange.IO(("rate", "CreateOrder", 4, "1s")); loop { if let Ok(ticker) = exchange.GetTicker("BTC_USDT") { if ticker.Last < 50000.0 { let _ = exchange.CreateOrder("BTC_USDT", "buy", ticker.Last, 0.001); } } Sleep(100); } }c++// C++ not supported yet -
Scenario 2: Unified Rate Limiting Across Multiple Exchange Objects
javascriptfunction main() { // Set rate limiting for each exchange object for (var i = 0; i < exchanges.length; i++) { exchanges[i].IO("rate", "GetTicker", 10, "1s") exchanges[i].IO("rate", "CreateOrder", 2, "1s") } // Concurrently fetch tickers from multiple exchanges while (true) { for (var i = 0; i < exchanges.length; i++) { var ticker = exchanges[i].GetTicker("BTC_USDT") if (ticker) { Log(exchanges[i].GetName(), "Price:", ticker.Last) } } Sleep(1000) } }pythondef main(): # Set rate limiting for each exchange object for i in range(len(exchanges)): exchanges[i].IO("rate", "GetTicker", 10, "1s") exchanges[i].IO("rate", "CreateOrder", 2, "1s") # Concurrently fetch tickers from multiple exchanges while True: for i in range(len(exchanges)): ticker = exchanges[i].GetTicker("BTC_USDT") if ticker: Log(exchanges[i].GetName(), "Price:", ticker["Last"]) Sleep(1000)rustfn main() { // Set rate limiting for each exchange object for e in exchanges.iter() { let _ = e.IO(("rate", "GetTicker", 10, "1s")); let _ = e.IO(("rate", "CreateOrder", 2, "1s")); } // Concurrently fetch tickers from multiple exchanges loop { for e in exchanges.iter() { if let Ok(ticker) = e.GetTicker("BTC_USDT") { Log!(e.GetName(), "Price:", ticker.Last); } } Sleep(1000); } }c++// C++ is not supported yet -
Scenario 3: Intraday Quota Management
javascriptfunction main() { // Maximum 1000 API calls per day, resets at 08:00 every morning exchange.IO("quota", "*", 1000, "@0800") var callCount = 0 while (true) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { callCount++ Log("Call count:", callCount, "Price:", ticker.Last) } else { Log("Daily quota exceeded, waiting for tomorrow 08:00") Sleep(60000) // Wait 1 minute before retrying } Sleep(10000) } }pythondef main(): # Maximum 1000 API calls per day, resets at 08:00 every morning exchange.IO("quota", "*", 1000, "@0800") callCount = 0 while True: ticker = exchange.GetTicker("BTC_USDT") if ticker: callCount += 1 Log("Call count:", callCount, "Price:", ticker["Last"]) else: Log("Daily quota exceeded, waiting for tomorrow 08:00") Sleep(60000) # Wait 1 minute before retrying Sleep(10000)rustfn main() { // Maximum 1000 API calls per day, resets at 08:00 every morning let _ = exchange.IO(("quota", "*", 1000, "@0800")); let mut callCount = 0; loop { match exchange.GetTicker("BTC_USDT") { Ok(ticker) => { callCount += 1; Log!("Call count:", callCount, "Price:", ticker.Last); } Err(_) => { Log!("Daily quota exceeded, waiting for tomorrow 08:00"); Sleep(60000); // Wait 1 minute before retrying } } Sleep(10000); } }c++// C++ is not supported yet -
Scenario 4: Combined Rate Limiting Strategy
javascriptfunction main() { // Combine multiple rate limiting strategies // 1. Rate limit market data APIs per second exchange.IO("rate", "GetTicker,GetDepth", 20, "1s") // 2. Rate limit trading APIs per second exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s") // 3. Rate limit account query APIs per minute exchange.IO("rate", "GetAccount,GetPositions", 30, "1m") // 4. Total daily quota for all APIs exchange.IO("quota", "*", 10000, "@0000") Log("Multi-level rate limiting configured") // Main strategy loop while (true) { // Fetch market data var ticker = exchange.GetTicker("BTC_USDT") var depth = exchange.GetDepth("BTC_USDT") // Query account info if (Date.now() % 60000 < 1000) { // Query once per minute var account = exchange.GetAccount() Log("Account:", account) } // Trading logic if (ticker && ticker.Last < 50000) { exchange.CreateOrder("BTC_USDT", "buy", ticker.Last, 0.001) } Sleep(500) } }pythonimport time def main(): # Combine multiple rate limiting strategies # 1. Rate limit market data APIs per second exchange.IO("rate", "GetTicker,GetDepth", 20, "1s") # 2. Rate limit trading APIs per second exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s") # 3. Rate limit account query APIs per minute exchange.IO("rate", "GetAccount,GetPositions", 30, "1m") # 4. Total daily quota for all APIs exchange.IO("quota", "*", 10000, "@0000") Log("Multi-level rate limiting configured") # Main strategy loop while True: # Fetch market data ticker = exchange.GetTicker("BTC_USDT") depth = exchange.GetDepth("BTC_USDT") # Query account info if int(time.time() * 1000) % 60000 < 1000: # Query once per minute account = exchange.GetAccount() Log("Account:", account) # Trading logic if ticker and ticker["Last"] < 50000: exchange.CreateOrder("BTC_USDT", "buy", ticker["Last"], 0.001) Sleep(500)rustfn main() { // Combine multiple rate limiting strategies // 1. Rate limit market data APIs per second let _ = exchange.IO(("rate", "GetTicker,GetDepth", 20, "1s")); // 2. Rate limit trading APIs per second let _ = exchange.IO(("rate", "CreateOrder,CancelOrder", 5, "1s")); // 3. Rate limit account query APIs per minute let _ = exchange.IO(("rate", "GetAccount,GetPositions", 30, "1m")); // 4. Total daily quota for all APIs let _ = exchange.IO(("quota", "*", 10000, "@0000")); Log!("Multi-level rate limiting configured"); // Main strategy loop loop { // Fetch market data let ticker = exchange.GetTicker("BTC_USDT"); let depth = exchange.GetDepth("BTC_USDT"); // Query account info if UnixNano() / 1000000 % 60000 < 1000 { // Query once per minute let account = exchange.GetAccount(); Log!("Account:", account); } // Trading logic if let Ok(t) = ticker { if t.Last < 50000.0 { let _ = exchange.CreateOrder("BTC_USDT", "buy", t.Last, 0.001); } } Sleep(500); } }c++// C++ not supported yet -
Notes
1. Time Window Alignment in quota Mode
The quota mode strictly aligns to time windows:
-
"1s": aligns to whole seconds (e.g., 12:00:00, 12:00:01, 12:00:02……) -
"1m": aligns to whole minutes (e.g., 12:00:00, 12:01:00, 12:02:00……) -
"1h": aligns to whole hours (e.g., 12:00:00, 13:00:00, 14:00:00……)
This means that even if counting starts at 12:00:00.500, the current time window will still reset at 12:00:01.000.
javascriptfunction main() { // quota mode: strictly aligns to whole seconds exchange.IO("quota", "GetTicker", 3, "1s") // Assume the current time is 12:00:00.500 exchange.GetTicker("BTC_USDT") // 1st call, success exchange.GetTicker("BTC_USDT") // 2nd call, success exchange.GetTicker("BTC_USDT") // 3rd call, success exchange.GetTicker("BTC_USDT") // 4th call, failed (limit exceeded) Sleep(500) // Wait 500ms; the time is now 12:00:01.000 // Window has been reset exchange.GetTicker("BTC_USDT") // 1st call in the new window, success }pythondef main(): # quota mode: strictly aligns to whole seconds exchange.IO("quota", "GetTicker", 3, "1s") # Assume the current time is 12:00:00.500 exchange.GetTicker("BTC_USDT") # 1st call, success exchange.GetTicker("BTC_USDT") # 2nd call, success exchange.GetTicker("BTC_USDT") # 3rd call, success exchange.GetTicker("BTC_USDT") # 4th call, failed (limit exceeded) Sleep(500) # Wait 500ms; the time is now 12:00:01.000 # Window has been reset exchange.GetTicker("BTC_USDT") # 1st call in the new window, successrustfn main() { // quota mode: strictly aligns to whole seconds let _ = exchange.IO(("quota", "GetTicker", 3, "1s")); // Assume the current time is 12:00:00.500 let _ = exchange.GetTicker("BTC_USDT"); // 1st call, success let _ = exchange.GetTicker("BTC_USDT"); // 2nd call, success let _ = exchange.GetTicker("BTC_USDT"); // 3rd call, success let _ = exchange.GetTicker("BTC_USDT"); // 4th call, failed (limit exceeded) Sleep(500); // Wait 500ms; the time is now 12:00:01.000 // Window has been reset let _ = exchange.GetTicker("BTC_USDT"); // 1st call in the new window, success }c++// C++ is not supported yet -
-
2. Time discrepancy in delay mode
When using the
"delay"parameter, the actual API call time may not match the time recorded in the log. This is because the program enters a waiting state when rate limiting is triggered, while the log records the time after the wait ends.javascriptfunction main() { exchange.IO("rate", "GetTicker", 2, "1s", "delay") Log(_D(), "Call 1") // 12:00:00.000 exchange.GetTicker("BTC_USDT") Log(_D(), "Call 2") // 12:00:00.100 exchange.GetTicker("BTC_USDT") Log(_D(), "Call 3") // 12:00:00.200, but it will actually wait until 12:00:01.000 exchange.GetTicker("BTC_USDT") // Triggers rate limiting, waits automatically Log(_D(), "Call 3 completed") // Log shows 12:00:01.000+ // It appears that 3 calls were made within one second, but the 3rd call was actually executed in a new window }pythondef main(): exchange.IO("rate", "GetTicker", 2, "1s", "delay") Log(_D(), "Call 1") # 12:00:00.000 exchange.GetTicker("BTC_USDT") Log(_D(), "Call 2") # 12:00:00.100 exchange.GetTicker("BTC_USDT") Log(_D(), "Call 3") # 12:00:00.200, but it will actually wait until 12:00:01.000 exchange.GetTicker("BTC_USDT") # Triggers rate limiting, waits automatically Log(_D(), "Call 3 completed") # Log shows 12:00:01.000+ # It appears that 3 calls were made within one second, but the 3rd call was actually executed in a new windowrustfn main() { let _ = exchange.IO(("rate", "GetTicker", 2, "1s", "delay")); Log!(_D(None), "Call 1"); // 12:00:00.000 let _ = exchange.GetTicker("BTC_USDT"); Log!(_D(None), "Call 2"); // 12:00:00.100 let _ = exchange.GetTicker("BTC_USDT"); Log!(_D(None), "Call 3"); // 12:00:00.200, but it will actually wait until 12:00:01.000 let _ = exchange.GetTicker("BTC_USDT"); // Triggers rate limiting, waits automatically Log!(_D(None), "Call 3 completed"); // Log shows 12:00:01.000+ // It appears that 3 calls were made within one second, but the 3rd call was actually executed in a new window }c++// C++ is not supported yet -
3. Rate limiting for the Buy/Sell functions
Both the
BuyandSellfunctions callCreateOrderunder the hood, so their rate-limiting rules follow theCreateOrdersettings.javascriptfunction main() { // Set CreateOrder rate limiting exchange.IO("rate", "CreateOrder", 5, "1s") // Buy and Sell are also subject to this limit for (var i = 0; i < 10; i++) { if (i % 2 == 0) { exchange.Buy(50000, 0.001) // Subject to the CreateOrder limit } else { exchange.Sell(51000, 0.001) // Subject to the CreateOrder limit } } }pythondef main(): # Set CreateOrder rate limiting exchange.IO("rate", "CreateOrder", 5, "1s") # Buy and Sell are also subject to this limit for i in range(10): if i % 2 == 0: exchange.Buy(50000, 0.001) # Subject to the CreateOrder limit else: exchange.Sell(51000, 0.001) # Subject to the CreateOrder limitrustfn main() { // Set CreateOrder rate limiting let _ = exchange.IO(("rate", "CreateOrder", 5, "1s")); // Buy and Sell are also subject to this limit for i in 0..10 { if i % 2 == 0 { let _ = exchange.Buy(50000, 0.001); // Subject to the CreateOrder limit } else { let _ = exchange.Sell(51000, 0.001); // Subject to the CreateOrder limit } } }c++// C++ is not supported yet -
4. Rate Limiting for Go Functions
Rate limiting for the
Gofunction depends on the actual function being called concurrently.javascriptfunction main() { // Rate limit GetTicker exchange.IO("rate", "GetTicker", 5, "1s") // Concurrent calls to GetTicker are rate limited var tasks = [] for (var i = 0; i < 10; i++) { tasks.push(exchange.Go("GetTicker", "BTC_USDT")) } for (var i = 0; i < tasks.length; i++) { var ticker = tasks[i].wait() Log("Task", i, ticker ? "Success" : "Rate limited") } }pythondef main(): # Rate limit GetTicker exchange.IO("rate", "GetTicker", 5, "1s") # Concurrent calls to GetTicker are rate limited tasks = [] for i in range(10): tasks.append(exchange.Go("GetTicker", "BTC_USDT")) for i in range(len(tasks)): ticker = tasks[i].wait() Log("Task", i, "Success" if ticker else "Rate limited")rustfn main() { // Rate limit GetTicker let _ = exchange.IO(("rate", "GetTicker", 5, "1s")); // Concurrent calls to GetTicker are rate limited // In Rust, exchange.Go uses a typed syntax with the Go::GetTicker token let mut tasks = Vec::new(); for _i in 0..10 { tasks.push(exchange.Go(Go::GetTicker, ("BTC_USDT",))); } for (i, task) in tasks.iter().enumerate() { match task.wait(0) { Ok(_) => Log!("Task", i, "Success"), Err(_) => Log!("Task", i, "Rate limited"), } } }c++// C++ is not supported yet -
5. Rate Limiting for IO/api
IO/apirate limiting only takes effect onexchange.IO("api", ...)calls, and does not affect otherexchange.IO
functions.javascriptfunction main() { // Limit exchange.IO("api", ...) calls exchange.IO("rate", "IO/api", 10, "1s") // Rate limited for (var i = 0; i < 15; i++) { var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "") Log("API call", i, ret ? "Success" : "Rate limited") } // Not rate limited exchange.IO("currency", "LTC_USDT") // Switch trading pair, not rate limited exchange.IO("rate", "GetDepth", 5, "1s") // Set other rate limits, not rate limited }pythondef main(): # Limit exchange.IO("api", ...) calls exchange.IO("rate", "IO/api", 10, "1s") # Rate limited for i in range(15): ret = exchange.IO("api", "GET", "/api/v5/account/balance", "") Log("API call", i, "Success" if ret else "Rate limited") # Not rate limited exchange.IO("currency", "LTC_USDT") # Switch trading pair, not rate limited exchange.IO("rate", "GetDepth", 5, "1s") # Set other rate limits, not rate limitedrustfn main() { // Limit exchange.IO("api", ...) calls let _ = exchange.IO(("rate", "IO/api", 10, "1s")); // Rate limited for i in 0..15 { match exchange.IO(("api", "GET", "/api/v5/account/balance", "")) { Ok(_) => Log!("API call", i, "Success"), Err(_) => Log!("API call", i, "Rate limited"), } } // Not rate limited let _ = exchange.IO(("currency", "LTC_USDT")); // Switch trading pair, not rate limited let _ = exchange.IO(("rate", "GetDepth", 5, "1s")); // Set other rate limits, not rate limited }c++// C++ is not supported yet -
Best Practices
1. Set according to exchange limits: Please refer to the exchange's API documentation and set the rate limit value slightly below the exchange's limit.
2. Leave a safety margin: Do not set the rate limit value to the maximum allowed by the exchange; it is recommended to set it to 70%-80% of the maximum.
3. Tiered rate limiting: Set different rate limit values for different types of APIs, and reserve a larger margin for important APIs.
4. Use delay mode for critical calls: For API calls that must succeed, use
"delay"mode to ensure the call succeeds.5. Monitor API usage: Regularly check the strategy's API call frequency and continuously optimize the call logic.
6. Avoid excessive calls: Design the strategy logic reasonably to avoid unnecessary API calls.
7. Test rate limit configuration: Before running live, test whether the rate limit configuration is reasonable in a simulated environment.
See Also
Communication Between Live Trading Strategies
Overview
The inter-strategy communication feature allows different live trading strategies to share data and synchronize state with one another. Through a channel mechanism, one live strategy can broadcast its own state data to other live strategies, enabling data communication across strategies, across managers, and across servers.
Core Concepts
-
Channel: Every live strategy owns an independent channel, and the channel ID is the live strategy ID
-
Broadcaster: A live strategy that publishes data on a channel using the
SetChannelData()function -
Subscriber: A live strategy that subscribes to another live strategy's channel data using the
GetChannelData()function -
State Overwrite: A channel retains only the latest state; new data overwrites old data rather than using a message queue mechanism
Key Features
-
Non-blocking Communication: All function calls are non-blocking and will not affect the strategy's main workflow
-
Cross-platform Support: Supports data transmission across strategies, across managers, and across servers
-
Multi-channel Subscription: A single live strategy can subscribe to the channels of multiple different live strategies simultaneously
-
Flexible Data Format: Supports any JSON-serializable data structure
Use Cases
-
Master-slave Strategy Coordination: The master strategy analyzes the market and broadcasts signals, while slave strategies receive the signals and execute trades
-
Multi-account Synchronization: Synchronize trading signals and position information across multiple trading accounts
-
Strategy Monitoring: Broadcast strategy running status, which a monitoring live strategy subscribes to for display or alerting
-
Data Sharing: Share results such as market analysis and indicator calculations to avoid redundant computation
Basic Usage
Examples
-
Broadcaster Example - Publishing Market Data
javascriptfunction main() { var updateId = 0 var robotId = _G() // Get the current live bot ID while(true) { // Fetch market data var ticker = exchange.GetTicker("BTC_USDT") if (!ticker) { Sleep(5000) continue } // Prepare the 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 the 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)) Sleep(60000) // Update the channel state once per minute } }pythondef main(): updateId = 0 robotId = _G() # Get the current live bot ID while True: # Fetch market data ticker = exchange.GetTicker("BTC_USDT") if not ticker: Sleep(5000) continue # Prepare the 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 the current channel state LogStatus("Channel Broadcaster [Bot ID: {}]\n".format(robotId) + "Update ID: #{}\n".format(channelState["updateId"]) + "Time: {}\n".format(_D(channelState["timestamp"])) + "Last Price: ${:.2f}".format(channelState["lastPrice"])) Sleep(60000) # Update the channel state once per minuterustfn main() { let mut updateId = 0; let robotId = _G!(); // Get the current live bot ID loop { // Fetch market data let ticker = match exchange.GetTicker("BTC_USDT") { Ok(t) => t, Err(_) => { Sleep(5000); continue; } }; // Prepare the 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 the current channel state LogStatus!(format!( "Channel Broadcaster [Bot ID: {}]\nUpdate ID: #{}\nTime: {}\nSymbol: BTC_USDT\nLast Price: ${:.2}", robotId, updateId, _D(timestamp), ticker.Last )); Sleep(60000); // Update the channel state once per minute } }c++ -
Subscriber Example - Subscribe to Multiple Channels
javascriptfunction main() { // Get the IDs of the two channels to subscribe to (please modify according to your actual situation) var channelId1 = "632799" // Live trading ID of channel 1 var channelId2 = "632800" // Live trading ID of channel 2 while(true) { // Get the current state of channel 1 var state1 = GetChannelData(channelId1) // Get the current state of channel 2 var state2 = GetChannelData(channelId2) // Build the status display message var statusMsg = "Channel Subscriber - Current Subscription Status\n\n" // Display the status of channel 1 statusMsg += "═══ Channel1 [" + channelId1 + "] ═══\n" if (state1 !== null) { statusMsg += "Update ID: #" + state1.updateId + "\n" statusMsg += "Time: " + _D(state1.timestamp) + "\n" statusMsg += "Symbol: " + state1.symbol + "\n" statusMsg += "Last Price: $" + state1.lastPrice.toFixed(2) + "\n" } else { statusMsg += "Status: Waiting... (first call returns null)\n" } statusMsg += "\n" // Display the status of channel 2 statusMsg += "═══ Channel2 [" + channelId2 + "] ═══\n" if (state2 !== null) { statusMsg += "Update ID: #" + state2.updateId + "\n" statusMsg += "Time: " + _D(state2.timestamp) + "\n" statusMsg += "Last Price: $" + state2.lastPrice.toFixed(2) + "\n" } else { statusMsg += "Status: Waiting... (first call returns null)\n" } LogStatus(statusMsg) Sleep(5000) // Fetch channel data every 5 seconds } }pythondef main(): # Get the IDs of the two channels to subscribe to (please modify according to your actual situation) channelId1 = "632799" # Live trading ID of channel 1 channelId2 = "632800" # Live trading ID of channel 2 while True: # Get the current state of channel 1 state1 = GetChannelData(channelId1) # Get the current state of channel 2 state2 = GetChannelData(channelId2) # Build the status display message statusMsg = "Channel Subscriber - Current Subscription Status\n\n" # Display the status of channel 1 statusMsg += "═══ Channel1 [{}] ═══\n".format(channelId1) if state1 is not None: statusMsg += "Update ID: #{}\n".format(state1["updateId"]) statusMsg += "Time: {}\n".format(_D(state1["timestamp"])) statusMsg += "Last Price: ${:.2f}\n".format(state1["lastPrice"]) else: statusMsg += "Status: Waiting... (first call returns None)\n" statusMsg += "\n" # Display the status of channel 2 statusMsg += "═══ Channel2 [{}] ═══\n".format(channelId2) if state2 is not None: statusMsg += "Update ID: #{}\n".format(state2["updateId"]) statusMsg += "Time: {}\n".format(_D(state2["timestamp"])) statusMsg += "Last Price: ${:.2f}\n".format(state2["lastPrice"]) else: statusMsg += "Status: Waiting... (first call returns None)\n" LogStatus(statusMsg) Sleep(5000) # Fetch channel data every 5 secondsrustfn main() { // Rust's GetChannelData() function does not accept a channel ID parameter and cannot subscribe to channels of other live trading bots, // it can only read the latest data of the current live trading bot's own channel (i.e. the data this bot publishes via SetChannelData()) loop { // Read the current state of this bot's channel let state = GetChannelData(); // Build the status display message let mut statusMsg = String::from("Channel Subscriber - Current Subscription Status\n\n"); if !state.is_null() { statusMsg += &format!("Update ID: #{}\n", state["updateId"].as_f64().unwrap_or(0.0)); statusMsg += &format!("Time: {}\n", _D(state["timestamp"].as_i64().unwrap_or(0))); statusMsg += &format!("Symbol: {}\n", state["symbol"].as_str().unwrap_or("")); statusMsg += &format!("Last Price: ${:.2}\n", state["lastPrice"].as_f64().unwrap_or(0.0)); } else { statusMsg += "Status: Waiting... (first call returns null)\n"; } LogStatus!(statusMsg); Sleep(5000); // Read channel data every 5 seconds } }c++ -
Practical Application Scenarios
Scenario 1: Master-Slave Strategy Collaborative Trading
Master Strategy (Signal Broadcasting End)
javascriptfunction main() { var robotId = _G() Log("Main strategy started, Bot ID:", robotId) while(true) { // Analyze market conditions and generate trading signals var records = exchange.GetRecords("BTC_USDT") if (!records || records.length < 20) { Sleep(5000) continue } // Simple moving average crossover strategy var ma5 = TA.MA(records, 5) var ma20 = TA.MA(records, 20) var signal = "HOLD" if (ma5[ma5.length-1] > ma20[ma20.length-1] && ma5[ma5.length-2] <= ma20[ma20.length-2]) { signal = "BUY" } else if (ma5[ma5.length-1] < ma20[ma20.length-1] && ma5[ma5.length-2] >= ma20[ma20.length-2]) { signal = "SELL" } // Broadcast the trading signal var signalData = { timestamp: Date.now(), symbol: "BTC_USDT", signal: signal, price: records[records.length-1].Close, ma5: ma5[ma5.length-1], ma20: ma20[ma20.length-1] } SetChannelData(signalData) LogStatus("Main Strategy - Signal Broadcast\n" + "Signal: " + signal + "\n" + "Price: $" + signalData.price.toFixed(2) + "\n" + "MA5: " + signalData.ma5.toFixed(2) + "\n" + "MA20: " + signalData.ma20.toFixed(2)) Sleep(60000) } }pythondef main(): robotId = _G() Log("Main strategy started, Bot ID:", robotId) while True: # Analyze market conditions and generate trading signals records = exchange.GetRecords("BTC_USDT") if not records or len(records) < 20: Sleep(5000) continue # Simple moving average crossover strategy ma5 = TA.MA(records, 5) ma20 = TA.MA(records, 20) signal = "HOLD" if ma5[-1] > ma20[-1] and ma5[-2] <= ma20[-2]: signal = "BUY" elif ma5[-1] < ma20[-1] and ma5[-2] >= ma20[-2]: signal = "SELL" # Broadcast the trading signal signalData = { "timestamp": time.time() * 1000, "symbol": "BTC_USDT", "signal": signal, "price": records[-1]["Close"], "ma5": ma5[-1], "ma20": ma20[-1] } SetChannelData(signalData) LogStatus("Main Strategy - Signal Broadcast\n" + "Signal: {}\n".format(signal) + "Price: ${:.2f}\n".format(signalData["price"]) + "MA5: {:.2f}\n".format(signalData["ma5"]) + "MA20: {:.2f}".format(signalData["ma20"])) Sleep(60000)rustfn main() { let robotId = _G!(); Log!("Main strategy started, Bot ID:", robotId); loop { // Analyze market conditions and generate trading signals let records = match exchange.GetRecords("BTC_USDT", None, None) { Ok(r) if r.len() >= 20 => r, _ => { Sleep(5000); continue; } }; // Simple moving average crossover strategy let ma5 = TA.MA(&records, 5); let ma20 = TA.MA(&records, 20); let n = ma5.len(); let mut signal = "HOLD"; if ma5[n - 1] > ma20[n - 1] && ma5[n - 2] <= ma20[n - 2] { signal = "BUY"; } else if ma5[n - 1] < ma20[n - 1] && ma5[n - 2] >= ma20[n - 2] { signal = "SELL"; } // Broadcast the trading signal // Rust's SetChannelData only accepts a string argument, so use format! to build the JSON text let price = records[records.len() - 1].Close; let signalData = format!( r#"{{"timestamp": {}, "symbol": "BTC_USDT", "signal": "{}", "price": {}, "ma5": {}, "ma20": {}}}"#, Unix() * 1000, signal, price, ma5[n - 1], ma20[n - 1] ); SetChannelData(&signalData); LogStatus!(format!( "Main Strategy - Signal Broadcast\nSignal: {}\nPrice: ${:.2}\nMA5: {:.2}\nMA20: {:.2}", signal, price, ma5[n - 1], ma20[n - 1] )); Sleep(60000); } }c++ -
Real-World Application Scenarios
Scenario 1: Master-Follower Strategy Coordinated Trading
Follower Strategy (Signal Receiving and Execution End)
javascriptfunction main() { var masterRobotId = "632799" // Live trading ID of the master strategy var lastSignal = null Log("Follower strategy started, subscribing to main strategy:", masterRobotId) while(true) { // Get the signal from the master strategy var signalData = GetChannelData(masterRobotId) if (signalData === null) { LogStatus("Waiting for main strategy signal...") Sleep(5000) continue } // Check whether there is a new signal if (lastSignal !== signalData.signal) { Log("Received new signal:", signalData.signal, "Price:", signalData.price) // Execute the trade if (signalData.signal === "BUY") { var ticker = exchange.GetTicker(signalData.symbol) if (ticker) { exchange.Buy(ticker.Last, 0.01) Log("Executing buy, Price:", ticker.Last) } } else if (signalData.signal === "SELL") { var ticker = exchange.GetTicker(signalData.symbol) if (ticker) { exchange.Sell(ticker.Last, 0.01) Log("Executing sell, Price:", ticker.Last) } } lastSignal = signalData.signal } LogStatus("Follower Strategy - Following Main Strategy\n" + "Current Signal: " + signalData.signal + "\n" + "Signal Price: $" + signalData.price.toFixed(2) + "\n" + "Signal Time: " + _D(signalData.timestamp)) Sleep(5000) } }pythondef main(): masterRobotId = "632799" # Live trading ID of the master strategy lastSignal = None Log("Follower strategy started, subscribing to main strategy:", masterRobotId) while True: # Get the signal from the master strategy signalData = GetChannelData(masterRobotId) if signalData is None: LogStatus("Waiting for main strategy signal...") Sleep(5000) continue # Check whether there is a new signal if lastSignal != signalData["signal"]: Log("Received new signal:", signalData["signal"], "Price:", signalData["price"]) # Execute the trade if signalData["signal"] == "BUY": ticker = exchange.GetTicker(signalData["symbol"]) if ticker: exchange.Buy(ticker["Last"], 0.01) Log("Executing buy, Price:", ticker["Last"]) elif signalData["signal"] == "SELL": ticker = exchange.GetTicker(signalData["symbol"]) if ticker: exchange.Sell(ticker["Last"], 0.01) Log("Executing sell, Price:", ticker["Last"]) lastSignal = signalData["signal"] LogStatus("Follower Strategy - Following Main Strategy\n" + "Current Signal: {}\n".format(signalData["signal"]) + "Signal Price: ${:.2f}\n".format(signalData["price"]) + "Signal Time: {}".format(_D(signalData["timestamp"]))) Sleep(5000)rustfn main() { // Rust's GetChannelData() function does not accept a channel ID parameter, so it cannot subscribe to the master strategy's live trading channel, // it can only read the latest data from the current live trading instance's own channel (this demonstrates equivalent signal-processing logic) let mut lastSignal = String::new(); Log!("Follower strategy started"); loop { // Get the signal from the channel let signalData = GetChannelData(); if signalData.is_null() { LogStatus!("Waiting for signal..."); Sleep(5000); continue; } let signal = signalData["signal"].as_str().unwrap_or("").to_string(); let price = signalData["price"].as_f64().unwrap_or(0.0); let symbol = signalData["symbol"].as_str().unwrap_or("BTC_USDT").to_string(); // Check whether there is a new signal if lastSignal != signal { Log!("Received new signal:", &signal, "Price:", price); // Execute the trade if signal == "BUY" { if let Ok(ticker) = exchange.GetTicker(symbol.as_str()) { let _ = exchange.Buy(ticker.Last, 0.01); Log!("Executing buy, Price:", ticker.Last); } } else if signal == "SELL" { if let Ok(ticker) = exchange.GetTicker(symbol.as_str()) { let _ = exchange.Sell(ticker.Last, 0.01); Log!("Executing sell, Price:", ticker.Last); } } lastSignal = signal.clone(); } LogStatus!(format!( "Follower Strategy\nCurrent Signal: {}\nSignal Price: ${:.2}\nSignal Time: {}", signal, price, _D(signalData["timestamp"].as_i64().unwrap_or(0)) )); Sleep(5000); } }c++ -
Scenario 2: Multi-Strategy Status Monitoring
Monitoring Strategies
javascriptfunction main() { // List of live-trading bot IDs to monitor var monitorList = ["632799", "632800", "632801"] while(true) { var table = { type: "table", title: "Strategy Running Status Monitor", cols: ["Bot ID", "Status", "Last Update", "Symbol", "Current Price", "PnL"], rows: [] } for (var i = 0; i < monitorList.length; i++) { var robotId = monitorList[i] var data = GetChannelData(robotId) if (data !== null) { var updateTime = _D(data.timestamp) var timeDiff = Date.now() - data.timestamp var status = timeDiff < 120000 ? "Running" : "Error" table.rows.push([ robotId, status, updateTime, data.symbol || "-", data.lastPrice ? "$" + data.lastPrice.toFixed(2) : "-", data.profit ? data.profit.toFixed(2) + "%" : "-" ]) } else { table.rows.push([ robotId, "Waiting for Data", "-", "-", "-", "-" ]) } } LogStatus("`" + JSON.stringify(table) + "`") Sleep(10000) } }pythondef main(): # List of live-trading bot IDs to monitor monitorList = ["632799", "632800", "632801"] while True: table = { "type": "table", "title": "Strategy Running Status Monitor", "cols": ["Bot ID", "Status", "Last Update", "Symbol", "Current Price", "PnL"], "rows": [] } for robotId in monitorList: data = GetChannelData(robotId) if data is not None: updateTime = _D(data["timestamp"]) timeDiff = time.time() * 1000 - data["timestamp"] status = "Running" if timeDiff < 120000 else "Error" table["rows"].append([ robotId, status, updateTime, data.get("symbol", "-"), "${:.2f}".format(data["lastPrice"]) if "lastPrice" in data else "-", "{:.2f}%".format(data["profit"]) if "profit" in data else "-" ]) else: table["rows"].append([ robotId, "Waiting for Data", "-", "-", "-", "-" ]) LogStatus("`" + json.dumps(table) + "`") Sleep(10000)rustfn main() { // Rust's GetChannelData() function does not accept a channel ID parameter, so it cannot subscribe to other bots' channels for monitoring; // it can only read the latest data from the current bot's own channel (this demonstrates the equivalent status-table display logic) loop { let data = GetChannelData(); let row = if !data.is_null() { let timestamp = data["timestamp"].as_i64().unwrap_or(0); let updateTime = _D(timestamp); let timeDiff = Unix() * 1000 - timestamp; let status = if timeDiff < 120000 { "Running" } else { "Error" }; format!( r#"["{}", "{}", "{}", "{}"]"#, _G!(), status, updateTime, data["symbol"].as_str().unwrap_or("-") ) } else { format!(r#"["{}", "Waiting for Data", "-", "-"]"#, _G!()) }; // Build the table JSON text (Rust has no JSON serialization, so use format! to concatenate) let table = format!( r#"{{"type": "table", "title": "Strategy Running Status Monitor", "cols": ["Bot ID", "Status", "Last Update", "Symbol"], "rows": [{}]}}"#, row ); LogStatus!(format!("`{}`", table)); Sleep(10000); } }c++ -
API Function Reference
### SetChannelData(data)
Function: Publishes the latest status data to a channel
Parameters:
- data: The data to publish, which can be any JSON-serializable data structure
Return Value: None
Characteristics:
- Non-blocking call
- Overwrites the previous data; does not accumulate historical records
- Automatically uses the current live trading ID as the channel ID
Data Size Limit:
- Must not exceed 1024 bytes after JSON serialization
- It is recommended to transmit only the necessary status information
Detailed Documentation: SetChannelData
### GetChannelData(robotId)
Function: Subscribes to the channel data of a specified live trading bot
Parameters:
- robotId: The live trading ID to subscribe to (string or number)
Return Value:
- Returns null on the first call; a retry is required
- Returns the latest data of the channel upon success
Characteristics:
- Non-blocking call
- Supports subscribing to multiple channels
- Supports subscribing to its own channel
Detailed Documentation: GetChannelData
## Notes
- First call returns null: The
GetChannelData()function returnsnullon its first call. This is normal behavior, as it needs to wait for data synchronization to complete. It is recommended to add a null check in your code.- Data overwrite mechanism: Only the latest status is stored on the channel. Calling
SetChannelData()overwrites the previous data. If you need to preserve historical data, you should record it yourself on the subscriber side.- Non-blocking characteristic: All channel communication functions are non-blocking calls and will not affect the execution of the strategy's main flow. However, this also means the real-time delivery of data cannot be guaranteed.
- Data size limit: The data passed to SetChannelData must not exceed 1024 bytes after JSON serialization. You should transmit only the necessary status information, such as key data like trading signals, prices, and positions, and avoid transmitting complete candlestick (K-line) arrays or large amounts of historical data.
- Live trading environment limitation: The channel communication feature is primarily intended for the live trading environment and may be restricted or unavailable in the backtesting system.
- Obtaining the live trading ID: You can obtain the current live trading ID via the
_G()function, or view it in the platform interface.- Security considerations: Channel data may be subscribed to by other live trading bots with the appropriate permissions, so do not transmit sensitive information (such as API keys) through the channel.
## Best Practices
- Reasonable update frequency: Set the data update frequency according to your actual needs to avoid wasting resources due to overly frequent updates.
- Data structure design: Design a clear data structure and include the necessary metadata (such as timestamps and version numbers) to facilitate processing on the subscriber side.
- Error handling: The subscriber side should handle null return values, and the broadcasting side should ensure the data format is correct.
- Status version control: Include a version number or update ID in the data to help the subscriber side determine whether new data is available.
- Monitoring and alerting: For critical communication links, it is recommended to implement timeout monitoring and alerting mechanisms.
- Testing and validation: Before using it in production, first validate the stability and latency of channel communication in a test environment.
- Documentation: Document the channel data format and communication protocol to facilitate future maintenance and team collaboration.
See Also
JavaScript Multi-threading
The FMZ Quant Trading Platform provides true multi-threading support for JavaScript language strategies from the system level, implementing the following objects:
| Object | Description | Notes |
|---|---|---|
| threading | Global multi-threading object | Member functions: Thread, getThread, mainThread, etc. |
| Thread | Thread object | Member functions: peekMessage, postMessage, join, etc. |
| ThreadLock | Thread lock object | Member functions: acquire, release. Can be passed as a parameter to thread execution functions into the thread environment. |
| ThreadEvent | Event object | Member functions: set, clear, wait, isSet. Can be passed as a parameter to thread execution functions into the thread environment. |
| ThreadCondition | Condition object | Member functions: notify, notifyAll, wait, acquire, release. Can be passed as a parameter to thread execution functions into the thread environment. |
| ThreadDict | Dictionary object | Member functions: get, set. Can be passed as a parameter to thread execution functions into the thread environment. |
FMZ Quant Trading Platform Syntax Manual: JavaScript Multi-threading
Web3
| Function Name | Description |
|---|---|
| exchange.IO("abi", ...) | Register ABI interface |
| exchange.IO("api", "eth", ...) | Call Ethereum RPC methods |
| exchange.IO("encode", ...) | Encode function calls |
| exchange.IO("encodePacked", ...) | Execute encodePacked encoding |
| exchange.IO("decode", ...) | Decode data |
| exchange.IO("key", ...) | Switch private key |
| exchange.IO("api", ...) | Call smart contract methods |
| exchange.IO("address") | Get current configured wallet address |
| exchange.IO("base", ...) | Set RPC node address |
TA Indicator Library
| Function Name | Description |
|---|---|
| TA.MACD | Calculate Moving Average Convergence Divergence indicator |
| TA.KDJ | Calculate Stochastic Oscillator indicator |
| TA.RSI | Calculate Relative Strength Index |
| TA.ATR | Calculate Average True Range indicator |
| TA.OBV | Calculate On Balance Volume indicator |
| TA.MA | Calculate Moving Average indicator |
| TA.EMA | Calculate Exponential Moving Average indicator |
| TA.BOLL | Calculate Bollinger Bands indicator |
| TA.Alligator | Calculate Alligator indicator |
| TA.CMF | Calculate Chaikin Money Flow indicator |
| TA.Highest | Calculate the highest price within specified period |
| TA.Lowest | Calculate the lowest price within specified period |
| TA.SMA | Calculate Simple Moving Average indicator |
talib Indicator Library
The talib indicator library contains numerous technical analysis indicators, for example: talib.CDL2CROWS. Please refer to the syntax manual for detailed information.