API限流控制

功能概述

API限流控制功能用于限制策略对交易所API的调用频率,防止触发交易所的频率限制导致账户被封禁或临时限制。FMZ平台提供了灵活的限流配置方式,支持两种限流模式和多种配置策略。

为什么需要API限流

  • 避免触发交易所限制:大多数交易所对API调用频率有严格限制,超限会导致账户被暂时或永久封禁。
  • 合理分配API配额:在多策略、多交易对场景下,需要合理分配API调用资源。
  • 提高策略稳定性:通过主动限流,避免因频繁调用导致的连接失败和数据获取异常。
  • 符合交易所规范:遵守交易所的API使用规范,维护良好的API使用关系。

两种限流模式

rate模式(平滑限流) - 适用于一般限流需求 - 不严格对齐时间窗口 - 调用分布相对平滑 - 推荐用于日常API调用限制

quota模式(额度限流) - 严格对齐时间窗口 - 例如设置"1s"时,窗口对齐到整秒;设置"1m"时,窗口对齐到整分钟 - 适用于需要严格控制时间窗口的场景 - 推荐用于日内配额管理

基本用法

rate模式基本示例

”`javascript function 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)
}

} python def 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++暂不支持
“`javascript function 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
}

} python def 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 limit```


// C++暂不支持

quota模式基本示例

”`javascript function 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")

} python def 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++暂不支持

函数名配置

单个函数限流

”`javascript function 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
    }
}

} python def 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 quota```


// C++暂不支持

多个函数联合限流

”`javascript function 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)

} python def 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++暂不支持

使用通配符限制所有函数


function 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
}

def main():
    # 不同时间周期的配置
    exchange.IO("rate", "GetTicker", 10, "1s")     # 每秒10次
    exchange.IO("rate", "GetDepth", 30, "1m")      # 每分钟30次
    exchange.IO("rate", "GetAccount", 100, "1h")   # 每小时100次
    exchange.IO("rate", "CreateOrder", 500, "1d")  # 每天500次

// C++暂不支持

时间周期配置

支持的时间单位

  • ns:纳秒
  • usµs:微秒
  • ms:毫秒
  • s:秒
  • m:分钟
  • h:小时
  • d:天

示例:"100ms", "1s", "5m", "1h", "1d" “`javascript function 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")

} python def 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++暂不支持

重置时间点配置

使用@HHMM@HHMMSS格式指定每日重置时间点,仅在quota模式下生效。 “`javascript function main() { exchange.IO(“rate”, “GetTicker”, 5, “1s”) // 不指定behavior参数

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")
        // 可以选择Sleep等待,或者跳过本次调用
        Sleep(200)
    }
}

} python def main(): exchange.IO(“rate”, “GetTicker”, 5, “1s”) # 不指定behavior参数

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")
        # 可以选择Sleep等待,或者跳过本次调用
        Sleep(200)```


// C++暂不支持

行为模式

默认模式(超限返回null)

”`javascript function main() { exchange.IO(“rate”, “GetTicker”, 5, “1s”, “delay”) // 指定delay参数

// 调用超限时会自动等待,确保每次调用都成功
for (var i = 0; i < 10; i++) {
    var ticker = exchange.GetTicker("BTC_USDT")
    Log("Call", i+1, "Success:", ticker.Last)  // ticker不会为null
}

} python def main(): exchange.IO(“rate”, “GetTicker”, 5, “1s”, “delay”) # 指定delay参数

# 调用超限时会自动等待,确保每次调用都成功
for i in range(10):
    ticker = exchange.GetTicker("BTC_USDT")
    Log("Call", i+1, "Success:", ticker["Last"])  # ticker不会为None```


// C++暂不支持

delay模式(超限自动等待)

支持的函数列表

交易类函数

  • CreateOrder:创建订单

  • CancelOrder:取消订单

  • Buy:买入(受CreateOrder限制)

  • Sell:卖出(受CreateOrder限制)

  • CreateConditionOrder:创建条件单

  • CancelConditionOrder:取消条件单

账户类函数

  • GetAccount:获取账户信息

  • GetAssets:获取资产信息

  • GetPositions:获取持仓信息

订单类函数

  • GetOrder:获取单个订单

  • GetOrders:获取所有订单

  • GetHistoryOrders:获取历史订单

  • GetConditionOrder:获取单个条件单

  • GetConditionOrders:获取所有条件单

  • GetHistoryConditionOrders:获取历史条件单

行情类函数

  • GetTicker:获取ticker数据

  • GetTickers:获取多个ticker数据

  • GetDepth:获取市场深度

  • GetRecords:获取K线数据

  • GetTrades:获取最新成交记录

其他函数

  • GetMarkets:获取市场列表

  • GetFundings:获取资金费率

  • SetMarginLevel:设置杠杆倍数

  • Go:并发调用(受实际调用函数限制)

  • IO/api:自定义API调用(仅限exchange.IO(“api”, …))

”`javascript function main() { // 假设交易所限制:GetTicker每秒20次,CreateOrder每秒5次 // 设置略低于交易所限制的值,留出安全余量 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)
}

} python def main(): # 假设交易所限制:GetTicker每秒20次,CreateOrder每秒5次 # 设置略低于交易所限制的值,留出安全余量 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++暂不支持

实际应用场景

场景1:防止触发交易所频率限制

”`javascript function main() { // 为每个交易所对象设置限流 for (var i = 0; i < exchanges.length; i++) { exchanges[i].IO(“rate”, “GetTicker”, 10, “1s”) exchanges[i].IO(“rate”, “CreateOrder”, 2, “1s”) }

// 并发获取多个交易所行情
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)
}

} python def main(): # 为每个交易所对象设置限流 for i in range(len(exchanges)): exchanges[i].IO(“rate”, “GetTicker”, 10, “1s”) exchanges[i].IO(“rate”, “CreateOrder”, 2, “1s”)

# 并发获取多个交易所行情
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++暂不支持

场景2:多交易所对象统一限流

”`javascript function main() { // 每天最多1000次API调用,每天早上8点重置 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)  // 等待1分钟后重试
    }
    Sleep(10000)
}

} python def main(): # 每天最多1000次API调用,每天早上8点重置 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)  # 等待1分钟后重试
    Sleep(10000)```


// C++暂不支持

场景3:日内配额管理

”`javascript function main() { // 组合多种限流策略 // 1. 行情类API每秒限制 exchange.IO(“rate”, “GetTicker,GetDepth”, 20, “1s”)

// 2. 交易类API每秒限制
exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s")

// 3. 账户查询每分钟限制
exchange.IO("rate", "GetAccount,GetPositions", 30, "1m")

// 4. 所有API每天总配额
exchange.IO("quota", "*", 10000, "@0000")

Log("Multi-level rate limiting configured")

// 策略主循环
while (true) {
    // 获取行情
    var ticker = exchange.GetTicker("BTC_USDT")
    var depth = exchange.GetDepth("BTC_USDT")

    // 查询账户
    if (Date.now() % 60000 < 1000) {  // 每分钟查询一次
        var account = exchange.GetAccount()
        Log("Account:", account)
    }

    // 交易逻辑
    if (ticker && ticker.Last < 50000) {
        exchange.CreateOrder("BTC_USDT", "buy", ticker.Last, 0.001)
    }

    Sleep(500)
}

} python import time def main(): # 组合多种限流策略 # 1. 行情类API每秒限制 exchange.IO(“rate”, “GetTicker,GetDepth”, 20, “1s”)

# 2. 交易类API每秒限制
exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s")

# 3. 账户查询每分钟限制
exchange.IO("rate", "GetAccount,GetPositions", 30, "1m")

# 4. 所有API每天总配额
exchange.IO("quota", "*", 10000, "@0000")

Log("Multi-level rate limiting configured")

# 策略主循环
while True:
    # 获取行情
    ticker = exchange.GetTicker("BTC_USDT")
    depth = exchange.GetDepth("BTC_USDT")

    # 查询账户
    if int(time.time() * 1000) % 60000 < 1000:  # 每分钟查询一次
        account = exchange.GetAccount()
        Log("Account:", account)

    # 交易逻辑
    if ticker and ticker["Last"] < 50000:
        exchange.CreateOrder("BTC_USDT", "buy", ticker["Last"], 0.001)

    Sleep(500)```


// C++暂不支持

场景4:组合限流策略

”`javascript function main() { // quota模式:严格对齐到整秒 exchange.IO(“quota”, “GetTicker”, 3, “1s”)

// 假设当前时间为 12:00:00.500
exchange.GetTicker("BTC_USDT")  // 第1次,成功
exchange.GetTicker("BTC_USDT")  // 第2次,成功
exchange.GetTicker("BTC_USDT")  // 第3次,成功
exchange.GetTicker("BTC_USDT")  // 第4次,失败(超限)

Sleep(500)  // 等待500ms,此时时间为 12:00:01.000

// 窗口已重置
exchange.GetTicker("BTC_USDT")  // 新窗口第1次,成功

} python def main(): # quota模式:严格对齐到整秒 exchange.IO(“quota”, “GetTicker”, 3, “1s”)

# 假设当前时间为 12:00:00.500
exchange.GetTicker("BTC_USDT")  # 第1次,成功
exchange.GetTicker("BTC_USDT")  # 第2次,成功
exchange.GetTicker("BTC_USDT")  # 第3次,成功
exchange.GetTicker("BTC_USDT")  # 第4次,失败(超限)

Sleep(500)  # 等待500ms,此时时间为 12:00:01.000

# 窗口已重置
exchange.GetTicker("BTC_USDT")  # 新窗口第1次,成功```


// C++暂不支持

注意事项

1. quota模式时间窗口对齐

quota模式严格对齐时间窗口:

  • "1s":对齐到整秒(例如:12:00:00, 12:00:01, 12:00:02…)
  • "1m":对齐到整分钟(例如:12:00:00, 12:01:00, 12:02:00…)
  • "1h":对齐到整小时(例如:12:00:00, 13:00:00, 14:00:00…)

这意味着即使在12:00:00.500开始计数,到12:00:01.000时窗口就会重置。 “`javascript function 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,但实际会等待到12:00:01.000
exchange.GetTicker("BTC_USDT")  // 触发限流,自动等待

Log(_D(), "Call 3 completed")  // 日志显示12:00:01.000+
// 看起来一秒内调用了3次,但实际第3次是在新窗口执行的

} python def 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,但实际会等待到12:00:01.000
exchange.GetTicker("BTC_USDT")  # 触发限流,自动等待

Log(_D(), "Call 3 completed")  # 日志显示12:00:01.000+
# 看起来一秒内调用了3次,但实际第3次是在新窗口执行的```


// C++暂不支持

2. delay模式下的时间差异

使用"delay"参数时,实际API调用时间与日志记录时间可能不一致。这是因为当触发限流时,程序会等待,但日志记录的是等待结束后的时间。 “`javascript function main() { // 设置CreateOrder限流 exchange.IO(“rate”, “CreateOrder”, 5, “1s”)

// Buy和Sell也会受到此限制
for (var i = 0; i < 10; i++) {
    if (i % 2 == 0) {
        exchange.Buy(50000, 0.001)   // 受CreateOrder限制
    } else {
        exchange.Sell(51000, 0.001)  // 受CreateOrder限制
    }
}

} python def main(): # 设置CreateOrder限流 exchange.IO(“rate”, “CreateOrder”, 5, “1s”)

# Buy和Sell也会受到此限制
for i in range(10):
    if i % 2 == 0:
        exchange.Buy(50000, 0.001)   # 受CreateOrder限制
    else:
        exchange.Sell(51000, 0.001)  # 受CreateOrder限制```


// C++暂不支持

3. Buy/Sell函数的限流

BuySell函数底层调用CreateOrder,因此它们的限流遵循CreateOrder的设置。 “`javascript function main() { // 限制GetTicker exchange.IO(“rate”, “GetTicker”, 5, “1s”)

// 并发调用GetTicker时受限
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")
}

} python def main(): # 限制GetTicker exchange.IO(“rate”, “GetTicker”, 5, “1s”)

# 并发调用GetTicker时受限
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++暂不支持

4. Go函数的限流

Go函数的限流取决于实际并发调用的函数。 “`javascript function main() { // 限制exchange.IO(“api”, …)调用 exchange.IO(“rate”, “IO/api”, 10, “1s”)

// 受限制
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")
}

// 不受限制
exchange.IO("currency", "LTC_USDT")  // 切换交易对,不受限
exchange.IO("rate", "GetDepth", 5, "1s")  // 设置其他限流,不受限

} python def main(): # 限制exchange.IO(“api”, …)调用 exchange.IO(“rate”, “IO/api”, 10, “1s”)

# 受限制
for i in range(15):
    ret = exchange.IO("api", "GET", "/api/v5/account/balance", "")
    Log("API call", i, "Success" if ret else "Rate limited")

# 不受限制
exchange.IO("currency", "LTC_USDT")  # 切换交易对,不受限
exchange.IO("rate", "GetDepth", 5, "1s")  # 设置其他限流,不受限```


// C++暂不支持

5. IO/api的限流

IO/api限流仅对exchange.IO("api", ...)调用生效,不影响其他exchange.IO功能。

最佳实践

1. **根据交易所限制进行设置**:参考交易所API文档,将限制值设置为略低于交易所规定的最大值。

2. **预留安全余量**:不要将限制设置为交易所的最大值,建议设置为最大值的70%-80%。

3. **实施分层限流**:针对不同类型的API设置不同的限制,为重要API预留更多余量。

4. **对关键调用使用delay模式**:对于必须成功的API调用,使用```"delay"```模式确保调用成功。

5. **监控API使用情况**:定期检查策略的API调用频率,持续优化调用逻辑。

6. **避免过度调用**:合理设计策略逻辑,减少不必要的API调用。

7. **测试限流配置**:在实盘交易前,先在模拟环境中测试限流配置的合理性。

{@fun/Trade/exchange.IO exchange.IO}, {@fun/Trade/exchange.Go exchange.Go}, {@fun/Trade/exchange.Buy exchange.Buy}, {@fun/Trade/exchange.Sell exchange.Sell}