Strategy Framework and API Functions
In strategies written in JavaScript, Python, or C++, the Sleep() function must be called within the main strategy loop. During backtesting, it controls the backtesting speed; during live trading, it controls the polling interval of the strategy, thereby managing the request frequency to the exchange API interfaces.
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)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)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)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
Feature Overview
The API rate limiting control feature is used to restrict the frequency of strategy calls to exchange APIs, preventing account bans or temporary restrictions caused by triggering exchange rate limits. The FMZ platform provides flexible rate limiting configuration methods, supporting two rate limiting modes and multiple configuration strategies.
Why API Rate Limiting is Needed
-
Avoid triggering exchange restrictions: Most exchanges have strict limits on API call frequency, and exceeding limits can result in temporary or permanent account bans.
-
Reasonable allocation of API quotas: In multi-strategy, multi-trading pair scenarios, API call resources need to be allocated reasonably.
-
Improve strategy stability: Through proactive rate limiting, avoid connection failures and data acquisition anomalies caused by frequent calls.
-
Comply with exchange specifications: Adhere to exchange API usage specifications and maintain good API usage relationships.
Two Rate Limiting Modes
rate mode (Smooth Rate Limiting)
- Suitable for general rate limiting needs
- Does not strictly align with time windows
- Relatively smooth call distribution
- Recommended for daily API call restrictions
quota mode (Quota Rate Limiting)
- Strictly aligns with 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 requiring strict time window control
- Recommended for intraday quota management
Basic Usage
Basic Example of rate Mode
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)c++// C++ 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 limitc++// C++ not supported yet -
Function Name Configuration
Single Function Rate Limiting
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")c++// C++ not supported yet -
Multiple Function Joint Rate Limiting
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 quotac++// C++ not supported yet -
Using Wildcards to Limit All Functions
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)c++// C++ not supported yet -
Time Period Configuration
Supported Time Units
ns: nanosecondsusorµs: microsecondsms: millisecondss: secondsm: minutesh: hoursd: days
Examples:
"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(): # 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 dayc++// C++ not supported yet -
Reset Time Point Configuration
Use
@HHMMor@HHMMSSformat to specify daily reset time point, only effective 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")c++// C++ not supported yet -
Behavior Modes
Default Mode (Return null when limit exceeded)
javascriptfunction main() { exchange.IO("rate", "GetTicker", 5, "1s") // Do not specify behavior parameter 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") // Can choose to Sleep and wait, or skip this call Sleep(200) } } }pythondef main(): exchange.IO("rate", "GetTicker", 5, "1s") # Do not specify behavior parameter 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") # Can choose to Sleep and wait, or skip this call Sleep(200)c++// C++ not supported yet -
Delay Mode (Auto wait when limit exceeded)
javascriptfunction main() { exchange.IO("rate", "GetTicker", 5, "1s", "delay") // Specify delay parameter // Will automatically wait when call limit exceeded, ensuring 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 delay parameter # Will automatically wait when call limit exceeded, ensuring every call succeeds for i in range(10): ticker = exchange.GetTicker("BTC_USDT") Log("Call", i+1, "Success:", ticker["Last"]) # ticker will not be Nonec++// C++ not supported yet -
Supported Function List
Trading Functions
-
CreateOrder: Create order -
CancelOrder: Cancel order -
Buy: Buy (subject to CreateOrder restrictions) -
Sell: Sell (subject to CreateOrder restrictions) -
CreateConditionOrder: Create conditional order -
CancelConditionOrder: Cancel conditional order
Account Functions
-
GetAccount: Get account information -
GetAssets: Get asset information -
GetPositions: Get position information
Order Functions
-
GetOrder: Get single order -
GetOrders: Get all orders -
GetHistoryOrders: Get historical orders -
GetConditionOrder: Get single conditional order -
GetConditionOrders: Get all conditional orders -
GetHistoryConditionOrders: Get historical conditional orders
Market Data Functions
-
GetTicker: Get ticker data -
GetTickers: Get multiple ticker data -
GetDepth: Get market depth -
GetRecords: Get K-line data -
GetTrades: Get latest trade records
Other Functions
-
GetMarkets: Get market list -
GetFundings: Get funding rates -
SetMarginLevel: Set leverage ratio -
Go: Concurrent call (subject to actual function call restrictions) -
IO/api: Custom API call (limited to exchange.IO("api", ...))
-
-
Practical Application Scenarios
Scenario 1: Prevent triggering exchange rate limits
javascriptfunction main() { // Assume exchange limits: GetTicker 20 times per second, CreateOrder 5 times per second // Set values slightly below exchange limits, leaving 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 values slightly below exchange limits, leaving 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)c++// C++ not supported yet -
Scenario 2: Unified Rate Limiting for 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 market data 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 market data 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)c++// C++ not supported yet -
Scenario 3: Daily Quota Management
javascriptfunction main() { // Maximum 1000 API calls per day, reset at 8:00 AM daily 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 retry } Sleep(10000) } }pythondef main(): # Maximum 1000 API calls per day, reset at 8:00 AM daily 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 retry Sleep(10000)c++// C++ not supported yet -
Scenario 4: Combined Rate Limiting Strategy
javascriptfunction main() { // Combine multiple rate limiting strategies // 1. Market data API rate limit per second exchange.IO("rate", "GetTicker,GetDepth", 20, "1s") // 2. Trading API rate limit per second exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s") // 3. Account query rate limit 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") // Strategy main loop while (true) { // Get market data var ticker = exchange.GetTicker("BTC_USDT") var depth = exchange.GetDepth("BTC_USDT") // Query account 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. Market data API rate limit per second exchange.IO("rate", "GetTicker,GetDepth", 20, "1s") # 2. Trading API rate limit per second exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s") # 3. Account query rate limit 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") # Strategy main loop while True: # Get market data ticker = exchange.GetTicker("BTC_USDT") depth = exchange.GetDepth("BTC_USDT") # Query account 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)c++// C++ not supported yet -
Important Notes
1. Quota Mode Time Window Alignment
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 even if counting starts at 12:00:00.500, the window will reset at 12:00:01.000.
javascriptfunction main() { // Quota mode: strictly aligns to whole seconds exchange.IO("quota", "GetTicker", 3, "1s") // Assume 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 (quota exceeded) Sleep(500) // Wait 500ms, now time is 12:00:01.000 // Window has reset exchange.GetTicker("BTC_USDT") // 1st call in new window, success }pythondef main(): # Quota mode: strictly aligns to whole seconds exchange.IO("quota", "GetTicker", 3, "1s") # Assume 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 (quota exceeded) Sleep(500) # Wait 500ms, now time is 12:00:01.000 # Window has reset exchange.GetTicker("BTC_USDT") # 1st call in new window, successc++// C++ not supported yet -
2. Time Discrepancy in Delay Mode
When using the
"delay"parameter, the actual API call time may differ from the logged time. This is because when rate limiting is triggered, the program waits, but 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 will actually wait until 12:00:01.000 exchange.GetTicker("BTC_USDT") // Rate limit triggered, auto wait Log(_D(), "Call 3 completed") // Log shows 12:00:01.000+ // Appears to have called 3 times in one second, but actually the 3rd call executed in the 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 will actually wait until 12:00:01.000 exchange.GetTicker("BTC_USDT") # Rate limit triggered, auto wait Log(_D(), "Call 3 completed") # Log shows 12:00:01.000+ # Appears to have called 3 times in one second, but actually the 3rd call executed in the new windowc++// C++ not supported yet -
3. Rate Limiting for Buy/Sell Functions
The
BuyandSellfunctions internally callCreateOrder, so their rate limiting follows theCreateOrdersettings.javascriptfunction main() { // Set CreateOrder rate limit 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 CreateOrder limit } else { exchange.Sell(51000, 0.001) // Subject to CreateOrder limit } } }pythondef main(): # Set CreateOrder rate limit 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 CreateOrder limit else: exchange.Sell(51000, 0.001) # Subject to CreateOrder limitc++// C++ not supported yet -
4. Rate Limiting for Go Functions
The rate limiting for
Gofunctions depends on the actual concurrently called functions.javascriptfunction main() { // Limit GetTicker exchange.IO("rate", "GetTicker", 5, "1s") // Subject to limits when calling GetTicker concurrently 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(): # Limit GetTicker exchange.IO("rate", "GetTicker", 5, "1s") # Subject to limits when calling GetTicker concurrently 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")c++// C++ not supported yet -
5. Rate Limiting for IO/api
IO/apirate limiting only applies toexchange.IO("api", ...)calls and does not affect otherexchange.IOfunctions.javascriptfunction main() { // Limit exchange.IO("api", ...) calls exchange.IO("rate", "IO/api", 10, "1s") // Subject to limits 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 subject to limits exchange.IO("currency", "LTC_USDT") // Switch trading pair, not limited exchange.IO("rate", "GetDepth", 5, "1s") // Set other rate limits, not limited }pythondef main(): # Limit exchange.IO("api", ...) calls exchange.IO("rate", "IO/api", 10, "1s") # Subject to limits 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 subject to limits exchange.IO("currency", "LTC_USDT") # Switch trading pair, not limited exchange.IO("rate", "GetDepth", 5, "1s") # Set other rate limits, not limitedc++// C++ not supported yet -
Best Practices
1. Configure based on exchange limitations: Refer to the exchange API documentation and set limit values slightly below the maximum values specified by the exchange.
2. Reserve safety margin: Do not set limits to the exchange's maximum values. It is recommended to set them to 70%-80% of the maximum values.
3. Implement tiered rate limiting: Set different limits for different types of APIs, reserving more capacity for important APIs.
4. Use delay mode for critical calls: For API calls that must succeed, use
"delay"mode to ensure call success.5. Monitor API usage: Regularly check the API call frequency of strategies and continuously optimize call logic.
6. Avoid excessive calls: Design strategy logic reasonably to reduce unnecessary API calls.
7. Test rate limiting configuration: Before live trading, test the reasonableness of rate limiting configuration in a simulation environment first.
See Also
Inter-Strategy Live Trading Communication
Feature Overview
The inter-strategy live trading communication feature allows different live trading strategies to share data and synchronize states. Through a channel mechanism, one live trading instance can broadcast its state data to other live trading instances, enabling cross-instance, cross-custodian, and cross-server data communication.
Core Concepts
- Channel: Each live trading instance has an independent channel, with the channel ID being the live trading instance ID
- Broadcaster: Live trading instance that publishes data on a channel using the
SetChannelData()function - Subscriber: Live trading instance that subscribes to other live trading instance channel data using the
GetChannelData()function - State Overwrite: Only the latest state is saved on the channel, new data overwrites old data, not a message queue
Key Features
- Non-blocking Communication: All function calls are non-blocking and do not affect the main strategy flow
- Cross-platform Support: Supports data transmission across live trading instances, custodians, and servers
- Multi-channel Subscription: A single live trading instance can simultaneously subscribe to multiple different live trading instance channels
- Flexible Data Format: Supports any JSON-serializable data structure
Application Scenarios
- Master-Slave Strategy Coordination: Master strategy analyzes market and broadcasts signals, slave strategies receive signals and execute trades
- Multi-account Synchronization: Synchronize trading signals and position information across multiple trading accounts
- Strategy Monitoring: Monitoring strategies broadcast operational status, monitoring live trading instances subscribe and display or alert
- Data Sharing: Share market analysis, indicator calculation results to avoid redundant calculations
Basic Usage
Examples
-
Broadcaster Example - Publishing Market Data
javascriptfunction main() { var updateId = 0 var robotId = _G() // Get current live trading ID while(true) { // Get market data var ticker = exchange.GetTicker("BTC_USDT") if (!ticker) { Sleep(5000) continue } // Prepare channel state data var channelState = { robotId: robotId, updateId: ++updateId, timestamp: Date.now(), symbol: "BTC_USDT", lastPrice: ticker.Last, volume: ticker.Volume, high: ticker.High, low: ticker.Low } // Publish latest state on channel (overwrite 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)) Sleep(60000) // Update channel state every minute } }pythondef main(): updateId = 0 robotId = _G() # Get current live trading ID while True: # Get market data ticker = exchange.GetTicker("BTC_USDT") if not ticker: Sleep(5000) continue # Prepare channel state data channelState = { "robotId": robotId, "updateId": updateId + 1, "timestamp": time.time() * 1000, "symbol": "BTC_USDT", "lastPrice": ticker["Last"], "volume": ticker["Volume"], "high": ticker["High"], "low": ticker["Low"] } updateId += 1 # Publish latest state on channel (overwrite 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"])) + "Last Price: ${:.2f}".format(channelState["lastPrice"])) Sleep(60000) # Update channel state every minutec++ -
Subscriber Example - Subscribe to Multiple Channels
javascriptfunction main() { // Two channel IDs to subscribe to (modify according to actual situation) var channelId1 = "632799" // Live trading ID for channel 1 var channelId2 = "632800" // Live trading ID for channel 2 while(true) { // Subscribe to current state of channel 1 var state1 = GetChannelData(channelId1) // Subscribe to current state of channel 2 var state2 = GetChannelData(channelId2) // Build status display var statusMsg = "Channel Subscriber - Current Subscription Status\n\n" // Display channel 1 status statusMsg += "═══ Channel 1 [" + channelId1 + "] ═══\n" if (state1 !== null) { statusMsg += "Update ID: #" + state1.updateId + "\n" statusMsg += "Time: " + _D(state1.timestamp) + "\n" statusMsg += "Trading Pair: " + state1.symbol + "\n" statusMsg += "Last Price: $" + state1.lastPrice.toFixed(2) + "\n" } else { statusMsg += "Status: Waiting... (first call returns null)\n" } statusMsg += "\n" // Display channel 2 status statusMsg += "═══ Channel 2 [" + channelId2 + "] ═══\n" if (state2 !== null) { statusMsg += "Update ID: #" + state2.updateId + "\n" statusMsg += "Time: " + _D(state2.timestamp) + "\n" statusMsg += "Last Price: $" + state2.lastPrice.toFixed(2) + "\n" } else { statusMsg += "Status: Waiting... (first call returns null)\n" } LogStatus(statusMsg) Sleep(5000) // Subscribe to channels every 5 seconds } }pythondef main(): # Two channel IDs to subscribe to (modify according to actual situation) channelId1 = "632799" # Live trading ID for channel 1 channelId2 = "632800" # Live trading ID for channel 2 while True: # Subscribe to current state of channel 1 state1 = GetChannelData(channelId1) # Subscribe to current state of channel 2 state2 = GetChannelData(channelId2) # Build status display statusMsg = "Channel Subscriber - Current Subscription Status\n\n" # Display channel 1 status statusMsg += "═══ Channel 1 [{}] ═══\n".format(channelId1) if state1 is not None: statusMsg += "Update ID: #{}\n".format(state1["updateId"]) statusMsg += "Time: {}\n".format(_D(state1["timestamp"])) statusMsg += "Last Price: ${:.2f}\n".format(state1["lastPrice"]) else: statusMsg += "Status: Waiting... (first call returns None)\n" statusMsg += "\n" # Display channel 2 status statusMsg += "═══ Channel 2 [{}] ═══\n".format(channelId2) if state2 is not None: statusMsg += "Update ID: #{}\n".format(state2["updateId"]) statusMsg += "Time: {}\n".format(_D(state2["timestamp"])) statusMsg += "Last Price: ${:.2f}\n".format(state2["lastPrice"]) else: statusMsg += "Status: Waiting... (first call returns None)\n" LogStatus(statusMsg) Sleep(5000) # Subscribe to channels every 5 secondsc++ -
Practical Application Scenarios
### Scenario 1: Master-Slave Strategy Coordinated Trading
Master Strategy (Signal Broadcasting Side)
javascriptfunction main() { var robotId = _G() Log("Main strategy started, Bot ID:", robotId) while(true) { // Analyze market and generate trading signals var records = exchange.GetRecords("BTC_USDT") if (!records || records.length < 20) { Sleep(5000) continue } // Simple moving average 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 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 and generate trading signals records = exchange.GetRecords("BTC_USDT") if not records or len(records) < 20: Sleep(5000) continue # Simple moving average 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 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)c++ -
Practical Application Scenarios
Scenario 1: Master-Slave Strategy Collaborative Trading
Slave Strategy (Signal Reception and Execution Side)
javascriptfunction main() { var masterRobotId = "632799" // Master strategy live trading ID var lastSignal = null Log("Follower strategy started, subscribing to main strategy:", masterRobotId) while(true) { // Get signal from master strategy var signalData = GetChannelData(masterRobotId) if (signalData === null) { LogStatus("Waiting for main strategy signal...") Sleep(5000) continue } // Check for new signal if (lastSignal !== signalData.signal) { Log("Received new signal:", signalData.signal, "Price:", signalData.price) // Execute 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" # Master strategy live trading ID lastSignal = None Log("Follower strategy started, subscribing to main strategy:", masterRobotId) while True: # Get signal from master strategy signalData = GetChannelData(masterRobotId) if signalData is None: LogStatus("Waiting for main strategy signal...") Sleep(5000) continue # Check for new signal if lastSignal != signalData["signal"]: Log("Received new signal:", signalData["signal"], "Price:", signalData["price"]) # Execute 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)c++ -
Scenario 2: Multi-Strategy Status Monitoring
Monitoring Strategy
javascriptfunction main() { // List of live trading strategy IDs to monitor var monitorList = ["632799", "632800", "632801"] while(true) { var table = { type: "table", title: "Strategy Running Status Monitor", cols: ["Live ID", "Status", "Last Update", "Trading Pair", "Current Price", "P&L"], 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" : "Abnormal" 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 strategy IDs to monitor monitorList = ["632799", "632800", "632801"] while True: table = { "type": "table", "title": "Strategy Running Status Monitor", "cols": ["Live ID", "Status", "Last Update", "Trading Pair", "Current Price", "P&L"], "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 "Abnormal" 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)c++ -
API Function Documentation
SetChannelData(data)
Function: Publish latest status data on the channel
Parameters:
- data: Data to be published, can be any JSON-serializable data structure
Return Value: None
Features:
-
Non-blocking call
-
Overwrites previous data, does not accumulate history
-
Automatically uses current live trading ID as channel ID
Data Length Limit:
-
Must not exceed 1024 bytes after JSON serialization
-
Recommend transmitting only necessary status information
Detailed Documentation: SetChannelData
GetChannelData(robotId)
Function: Subscribe to channel data of specified live trading instance
Parameters:
- robotId: Live trading ID to subscribe to (string or number)
Return Value:
-
Returns null on first call, retry required
-
Returns latest channel data after successful subscription
Features:
-
Non-blocking call
-
Can subscribe to multiple channels
-
Can subscribe to own channel
Detailed Documentation: GetChannelData
Important Notes
-
First Call Returns null: The
GetChannelData()function returnsnullon first call, which is normal behavior. Wait for data synchronization to complete. Recommend implementing null checks in code. -
Data Overwrite Mechanism: Channel only stores latest status, calling
SetChannelData()overwrites previous data. If historical data preservation is needed, subscribers should record it themselves. -
Non-blocking Characteristics: All channel communication functions are non-blocking and will not affect main strategy execution flow. However, this also means data immediacy cannot be guaranteed.
-
Data Size Limit: Data passed to SetChannelData must not exceed 1024 bytes after JSON serialization. Should only transmit necessary status information such as trading signals, prices, positions and other key data. Avoid transmitting complete candlestick arrays or large amounts of historical data.
-
Live Trading Environment Limitation: Channel communication functionality is primarily designed for live trading environments and may be limited or unavailable in backtesting systems.
-
Live Trading ID Retrieval: Current live trading ID can be obtained through
_G()function, or viewed in platform interface. -
Security Considerations: Channel data may be subscribed to by other authorized live trading instances. Do not transmit sensitive information (such as API keys) through channels.
Best Practices
-
Reasonable Update Frequency: Set data update frequency based on actual needs, avoid overly frequent updates that waste resources.
-
Data Structure Design: Design clear data structures including necessary metadata (such as timestamps, version numbers, etc.) for easier subscriber processing.
-
Error Handling: Subscribers should handle null return values, broadcasters should ensure correct data format.
-
Status Version Control: Include version numbers or update IDs in data to help subscribers determine if there is new data.
-
Monitoring and Alerting: For critical communication links, recommend implementing timeout monitoring and alerting mechanisms.
-
Testing and Validation: Verify channel communication stability and latency in test environment before formal use.
-
Documentation: Document channel data formats and communication protocols for 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.