API限流控制

功能概述

优宽量化平台支持对商品期货交易所API调用进行频率限制,防止触发交易所的频率限制。通过合理配置API限流,可以有效避免因频繁调用导致的接口限制问题,保障策略稳定运行。

API限流功能支持两种限流模式:

  • rate模式(平滑限流):不严格对齐时间窗口,适用于一般限流需求。例如设置每秒10次,系统会平滑地控制调用频率。
  • quota模式(额度限流):严格对齐时间窗口,例如1m对齐到整分钟,1s对齐到整秒。适用于需要严格控制时间窗口的场景。

基础用法

函数签名

exchange.IO("rate", functionNames, maxCalls, period, [behavior])
exchange.IO("quota", functionNames, maxCalls, period, [behavior])

参数说明

参数 类型 必填 说明
mode string 限流模式:”rate”(平滑限流)或 “quota”(额度限流)
functionNames string 要限制的函数名,支持单个函数、多个函数(逗号分隔)、通配符(*表示所有函数)
maxCalls number 时间周期内允许的最大调用次数
period string 时间周期或重置时间点。支持时间单位:ns、us、µs、ms、s、m、h、d(例如:”1s”、”1m”、”1h”)。支持重置时间点:@HHMM 或 @HHMMSS(例如:”@0815”表示每天08:15重置)
behavior string 超限行为。默认为空(超限返回null并报错);设置为”delay”时超限会等待

支持的函数列表

优宽量化平台的API限流功能支持以下函数:

交易类(2个): - CreateOrder - 创建订单 - CancelOrder - 撤销订单

账户类(2个): - GetAccount - 获取账户信息 - GetPositions - 获取持仓信息

订单类(3个): - GetOrder - 获取订单信息 - GetOrders - 获取订单列表 - GetHistoryOrders - 获取历史订单

行情类(3个): - GetTicker - 获取行情Tick数据 - GetDepth - 获取深度数据 - GetRecords - 获取K线数据

其它(1个): - IO/api - 仅对exchange.IO("api", ...)调用生效

函数名称配置

单个函数限流

限制单个函数的调用频率:

”`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 限制GetTicker每秒最多调用3次
exchange.IO("rate", "GetTicker", 3, "1s")

for (var i = 0; i < 10; i++) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var ticker = exchange.GetTicker("rb888")
    if (ticker) {
        Log("Success:", ticker.Last)
    } else {
        Log("Rate limit exceeded", "#FF0000")
    }
    Sleep(100)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 限制GetTicker每秒最多调用3次
exchange.IO("rate", "GetTicker", 3, "1s")

for i in range(10):
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    ticker = exchange.GetTicker("rb888")
    if ticker:
        Log("Success:", ticker["Last"])
    else:
        Log("Rate limit exceeded", "#FF0000")
    Sleep(100)```


// C++暂不支持
“`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// GetTicker和GetDepth共享限制,合计每秒最多5次
exchange.IO("rate", "GetTicker,GetDepth", 5, "1s")

for (var i = 0; i < 10; i++) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    if (i % 2 == 0) {
        var ticker = exchange.GetTicker("m888")
        Log("GetTicker:", ticker ? "Success" : "Failed")
    } else {
        var depth = exchange.GetDepth("m888")
        Log("GetDepth:", depth ? "Success" : "Failed")
    }
    Sleep(100)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# GetTicker和GetDepth共享限制,合计每秒最多5次
exchange.IO("rate", "GetTicker,GetDepth", 5, "1s")

for i in range(10):
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    if i % 2 == 0:
        ticker = exchange.GetTicker("m888")
        Log("GetTicker:", "Success" if ticker else "Failed")
    else:
        depth = exchange.GetDepth("m888")
        Log("GetDepth:", "Success" if depth else "Failed")
    Sleep(100)```


// C++暂不支持

多个函数联合限流

多个函数共享同一个限流配额: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 限制所有API调用每秒最多10次
exchange.IO("mode", 0)  // 建议配合立即返回模式
exchange.IO("rate", "*", 10, "1s")

var symbols = ["rb888", "m888", "i888"]
for (var i = 0; i < 5; i++) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var symbol = symbols[i % symbols.length]
    var ticker = exchange.GetTicker(symbol)
    var account = exchange.GetAccount()

    Log("Round", i+1, "Ticker:", ticker ? "✓" : "✗",
        "Account:", account ? "✓" : "✗")
    Sleep(500)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 限制所有API调用每秒最多10次
exchange.IO("mode", 0)  # 建议配合立即返回模式
exchange.IO("rate", "*", 10, "1s")

symbols = ["rb888", "m888", "i888"]
for i in range(5):
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    symbol = symbols[i % len(symbols)]
    ticker = exchange.GetTicker(symbol)
    account = exchange.GetAccount()

    Log("Round", i+1, "Ticker:", "✓" if ticker else "✗",
        "Account:", "✓" if account else "✗")
    Sleep(500)```


// C++暂不支持

通配符限流

使用通配符*限制所有API调用: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 不同时间周期的限流配置
exchange.IO("rate", "GetTicker", 10, "1s")    // 每秒10次
exchange.IO("rate", "GetDepth", 60, "1m")     // 每分钟60次
exchange.IO("rate", "GetAccount", 100, "1h")  // 每小时100次

Log("Rate limits configured")

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 不同时间周期的限流配置
exchange.IO("rate", "GetTicker", 10, "1s")    # 每秒10次
exchange.IO("rate", "GetDepth", 60, "1m")     # 每分钟60次
exchange.IO("rate", "GetAccount", 100, "1h")  # 每小时100次

Log("Rate limits configured")```


// C++暂不支持

时间周期配置

固定时间周期

支持多种时间单位:ns(纳秒)、us/µs(微秒)、ms(毫秒)、s(秒)、m(分钟)、h(小时)、d(天)。 “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 每天08:15重置配额,限制1000次
exchange.IO("quota", "GetTicker", 1000, "@0815")

Log("Daily quota configured: 1000 calls, reset at 08:15")

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 每天08:15重置配额,限制1000次
exchange.IO("quota", "GetTicker", 1000, "@0815")

Log("Daily quota configured: 1000 calls, reset at 08:15")```


// C++暂不支持

日内重置时间点

使用@HHMM@HHMMSS格式指定每日的重置时间: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 默认模式:超限返回null
exchange.IO("rate", "GetTicker", 3, "1s")

for (var i = 0; i < 10; i++) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var ticker = exchange.GetTicker("rb888")
    if (ticker) {
        Log("Call", i+1, "Success")
    } else {
        Log("Call", i+1, "Rate limit exceeded, returned null", "#FF0000")
    }
    Sleep(100)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 默认模式:超限返回null
exchange.IO("rate", "GetTicker", 3, "1s")

for i in range(10):
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    ticker = exchange.GetTicker("rb888")
    if ticker:
        Log("Call", i+1, "Success")
    else:
        Log("Call", i+1, "Rate limit exceeded, returned null", "#FF0000")
    Sleep(100)```


// C++暂不支持

行为模式配置

默认模式(返回 null)

超限时 API 调用返回 null,不等待: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// delay模式:超限自动等待
exchange.IO("rate", "GetTicker", 3, "1s", "delay")

Log("Testing delay mode", "#00FF00")
for (var i = 0; i < 10; i++) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var startTime = new Date().getTime()
    var ticker = exchange.GetTicker("m888")
    var endTime = new Date().getTime()

    Log("Call", i+1, "Time cost:", endTime - startTime, "ms")
}

} python import time def main(): while not exchange.IO(“status”): Sleep(1000)

# delay模式:超限自动等待
exchange.IO("rate", "GetTicker", 3, "1s", "delay")

Log("Testing delay mode", "#00FF00")
for i in range(10):
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    startTime = time.time() * 1000
    ticker = exchange.GetTicker("m888")
    endTime = time.time() * 1000

    Log("Call", i+1, "Time cost:", endTime - startTime, "ms")```


// C++暂不支持

delay模式(自动等待)

超限时自动等待,直到可以调用: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

exchange.IO("mode", 0)

// rate模式:平滑限流,不严格对齐时间窗口
// exchange.IO("rate", "GetTicker", 3, "1s")

// quota模式:严格对齐时间窗口,例如1s对齐到整秒
exchange.IO("quota", "GetTicker", 3, "1s")

for (var i = 0; i < 8; i++) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var ticker = exchange.GetTicker("c888")
    Log(_D(), "Call", i+1, ticker ? "Success" : "Quota exceeded")
    Sleep(150)  // 每150ms调用一次
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

exchange.IO("mode", 0)

# rate模式:平滑限流,不严格对齐时间窗口
# exchange.IO("rate", "GetTicker", 3, "1s")

# quota模式:严格对齐时间窗口,例如1s对齐到整秒
exchange.IO("quota", "GetTicker", 3, "1s")

for i in range(8):
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    ticker = exchange.GetTicker("c888")
    Log(_D(), "Call", i+1, "Success" if ticker else "Quota exceeded")
    Sleep(150)  # 每150ms调用一次```


// C++暂不支持

限流模式对比

rate模式 vs quota模式

”`javascript function main() { while (!exchange.IO(“status”)) { LogStatus(“Waiting for connection…”) Sleep(1000) }

// 设置行情获取频率限制
exchange.IO("rate", "GetTicker", 5, "1s", "delay")

Log("Monitoring rb888 (rebar) price")
while (true) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var ticker = exchange.GetTicker("rb888")
    LogStatus(_D(), "rb888 Price:", ticker.Last,
             "Buy:", ticker.Buy, "Sell:", ticker.Sell)
    Sleep(200)
}

} python def main(): while not exchange.IO(“status”): LogStatus(“Waiting for connection…”) Sleep(1000)

# 设置行情获取频率限制
exchange.IO("rate", "GetTicker", 5, "1s", "delay")

Log("Monitoring rb888 (rebar) price")
while True:
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    ticker = exchange.GetTicker("rb888")
    LogStatus(_D(), "rb888 Price:", ticker["Last"],
             "Buy:", ticker["Buy"], "Sell:", ticker["Sell"])
    Sleep(200)```


// C++暂不支持

实际应用场景

场景1:单品种行情监控

监控单个合约的行情,避免频繁调用: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

exchange.IO("mode", 0)
// 所有行情函数共享配额
exchange.IO("rate", "GetTicker,GetDepth", 10, "1s")

var symbols = ["rb888", "m888", "i888", "c888"]
Log("Monitoring multiple symbols:", symbols.join(", "))

while (true) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    for (var i = 0; i < symbols.length; i++) {
        var ticker = exchange.GetTicker(symbols[i])
        if (ticker) {
            Log(symbols[i], "Price:", ticker.Last)
        }
    }
    Sleep(1000)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

exchange.IO("mode", 0)
# 所有行情函数共享配额
exchange.IO("rate", "GetTicker,GetDepth", 10, "1s")

symbols = ["rb888", "m888", "i888", "c888"]
Log("Monitoring multiple symbols:", ", ".join(symbols))

while True:
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    for symbol in symbols:
        ticker = exchange.GetTicker(symbol)
        if ticker:
            Log(symbol, "Price:", ticker["Last"])
    Sleep(1000)```


// C++暂不支持

场景2:多品种轮询策略

轮询多个合约行情,控制总体调用频率: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 分别设置不同函数的限流
exchange.IO("rate", "GetTicker", 10, "1s", "delay")
exchange.IO("rate", "CreateOrder", 2, "1s", "delay")
exchange.IO("rate", "CancelOrder", 5, "1s", "delay")
exchange.IO("rate", "GetAccount", 1, "1s", "delay")

Log("Rate limits configured for high-frequency trading")

// 策略逻辑
while (true) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var ticker = exchange.GetTicker("rb888")
    var account = exchange.GetAccount()

    // 交易逻辑(示例)
    LogStatus(_D(), "Price:", ticker.Last, "Balance:", account.Balance)
    Sleep(500)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 分别设置不同函数的限流
exchange.IO("rate", "GetTicker", 10, "1s", "delay")
exchange.IO("rate", "CreateOrder", 2, "1s", "delay")
exchange.IO("rate", "CancelOrder", 5, "1s", "delay")
exchange.IO("rate", "GetAccount", 1, "1s", "delay")

Log("Rate limits configured for high-frequency trading")

# 策略逻辑
while True:
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    ticker = exchange.GetTicker("rb888")
    account = exchange.GetAccount()

    # 交易逻辑(示例)
    LogStatus(_D(), "Price:", ticker["Last"], "Balance:", account["Balance"])
    Sleep(500)```


// C++暂不支持

场景3:高频交易策略

对交易相关接口进行严格限流: “`javascript function main() { while (!exchange.IO(“status”)) { Sleep(1000) }

// 每天09:00重置,限制每日开仓100次
exchange.IO("quota", "CreateOrder", 100, "@0900")

Log("Daily order limit: 100 orders, reset at 09:00")

var orderCount = 0
while (true) {
    if (!exchange.IO("status")) {
        Sleep(5000)
        continue
    }

    var ticker = exchange.GetTicker("m888")

    // 交易逻辑(示例)
    // var orderId = exchange.CreateOrder(...)
    // if (orderId) {
    //     orderCount++
    //     Log("Order created, daily count:", orderCount)
    // } else {
    //     Log("Daily order quota exceeded", "#FF0000")
    // }

    Sleep(1000)
}

} python def main(): while not exchange.IO(“status”): Sleep(1000)

# 每天09:00重置,限制每日开仓100次
exchange.IO("quota", "CreateOrder", 100, "@0900")

Log("Daily order limit: 100 orders, reset at 09:00")

orderCount = 0
while True:
    if not exchange.IO("status"):
        Sleep(5000)
        continue

    ticker = exchange.GetTicker("m888")

    # 交易逻辑(示例)
    # orderId = exchange.CreateOrder(...)
    # if orderId:
    #     orderCount += 1
    #     Log("Order created, daily count:", orderCount)
    # else:
    #     Log("Daily order quota exceeded", "#FF0000")

    Sleep(1000)```


// C++暂不支持

场景4:日内交易次数控制

使用 quota 模式控制每日总交易次数:

注意事项

  • 必须检查连接状态

    • 使用API限流功能前,必须先通过exchange.IO("status")检查与交易服务器的连接状态
    • 在循环中持续检查连接状态,确保策略稳定运行
  • 配合立即返回模式

    • 建议配合exchange.IO("mode", 0)立即返回模式使用
    • 避免行情获取函数的等待影响限流效果
  • 商品期货市场

    • 优宽量化支持的市场为商品期货
    • 合约代码格式示例:rb888(螺纹钢)、m888(豆粕)、i888(铁矿石)、c888(玉米)
  • 限流配置时机

    • 建议在策略初始化阶段配置限流规则
    • 限流配置立即生效,无需重启策略
  • quota模式时间对齐

    • quota模式严格对齐时间窗口
    • 例如设置”1s”时,时间窗口对齐到整秒;设置”1m”时,时间窗口对齐到整分钟
  • delay模式的时间差异

    • 使用”delay”参数时,API调用时间与日志记录时间可能存在差异
    • 这是因为函数在等待限流窗口,属于正常现象
  • 合理设置限流参数

    • 根据交易所的实际限制设置参数
    • 预留一定余量,避免触及交易所硬限制

最佳实践

  • 分级限流策略

    • 对不同类型的API设置不同的限流参数
    • 重要的交易接口(CreateOrder、CancelOrder)设置更严格的限制
    • 行情接口可以相对宽松
  • 结合实际业务

    • 根据策略的实际调用频率设置限流
    • 监控实际调用情况,动态调整限流参数
  • 异常处理

    • 对返回null的情况进行妥善处理
    • 记录超限日志,便于后续分析优化
  • 测试验证

    • 在模拟环境中充分测试限流配置
    • 验证各种极端情况下的表现
  • 持续监控

    • 监控API调用频率和限流触发情况
    • 定期检查和优化限流配置