API限流控制

功能概述

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

为什么需要API限流

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

两种限流模式

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

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

基本用法

rate模式基本示例

”`javascript function main() { // 限制GetTicker每秒最多调用10次 exchange.IO(“rate”, “GetTicker”, 10, “1s”)

// 正常调用API
for (var i = 0; i < 20; i++) {
    var ticker = exchange.GetTicker("BTC_USDT")
    if (ticker) {
        Log("Success:", ticker.Last)
    } else {
        Log("Rate limit exceeded")  // 超过10次/秒时将返回null
    }
    Sleep(50)
}

} python def main(): # 限制GetTicker每秒最多调用10次 exchange.IO(“rate”, “GetTicker”, 10, “1s”)

# 正常调用API
for i in range(20):
    ticker = exchange.GetTicker("BTC_USDT")
    if ticker:
        Log("Success:", ticker["Last"])
    else:
        Log("Rate limit exceeded")  # 超过10次/秒时将返回None
    Sleep(50)```


// C++暂不支持此功能
“`javascript function main() { // 严格限制,时间窗口对齐至整秒 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)  // 每秒约6-7次调用,将触发限制
}

} python def main(): # 严格限制,时间窗口对齐至整秒 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)  # 每秒约6-7次调用,将触发限制```


// C++暂不支持此功能

quota模式基本示例

”`javascript function main() { // 仅限制GetTicker函数 exchange.IO(“rate”, “GetTicker”, 10, “1s”)

// GetTicker受限,GetDepth不受限
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")

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

# GetTicker受限,GetDepth不受限
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")```


// C++暂不支持

函数名配置

单个函数限流

”`javascript function main() { // GetTicker和GetDepth共享配额,合计每秒10次 exchange.IO(“rate”, “GetTicker,GetDepth”, 10, “1s”)

for (var i = 0; i < 15; i++) {
    if (i % 2 == 0) {
        exchange.GetTicker("BTC_USDT")  // 计入共享配额
    } else {
        exchange.GetDepth("BTC_USDT")   // 计入共享配额
    }
}

} python def main(): # GetTicker和GetDepth共享配额,合计每秒10次 exchange.IO(“rate”, “GetTicker,GetDepth”, 10, “1s”)

for i in range(15):
    if i % 2 == 0:
        exchange.GetTicker("BTC_USDT")  # 计入共享配额
    else:
        exchange.GetDepth("BTC_USDT")   # 计入共享配额```


// C++暂不支持

多个函数联合限流

”`javascript function main() { // 限制所有API调用,合计每分钟100次 exchange.IO(“rate”, “*”, 100, “1m”)

// 所有调用均计入总配额
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")
exchange.GetAccount()
exchange.CreateOrder("BTC_USDT", "buy", 50000, 0.001)

} python def main(): # 限制所有API调用,合计每分钟100次 exchange.IO(“rate”, “*”, 100, “1m”)

# 所有调用均计入总配额
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")
exchange.GetAccount()
exchange.CreateOrder("BTC_USDT", "buy", 50000, 0.001)```


// C++暂不支持

使用通配符限制所有函数


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

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() { // 每天08:15重置配额 exchange.IO(“quota”, “GetTicker”, 1000, “@0815”)

// 每天00:00重置配额
exchange.IO("quota", "CreateOrder", 500, "@0000")

// 每天23:59:59重置配额
exchange.IO("quota", "*", 5000, "@235959")

} python def main(): # 每天08:15重置配额 exchange.IO(“quota”, “GetTicker”, 1000, “@0815”)

# 每天00:00重置配额
exchange.IO("quota", "CreateOrder", 500, "@0000")

# 每天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:获取行情数据
  • GetTickers:获取多个交易对的行情数据
  • 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}