输入/搜索内容
入门
欢迎使用发明者量化交易平台
快速开始
密钥安全性
平台基础
账号与计费
实盘计费与充值
子账号
交易所
通用协议
本地凭据文件
交易所特殊说明
证券与期货
加密货币
托管者
策略库
实盘
编写策略
开发工具
回测系统
进阶专题
数据与研究
对外接口

交易所对API调用频率有限制,超限轻则请求被拒,重则账号被临时封禁。用exchange.IO("rate", ...)或exchange.IO("quota", ...)可以在托管者本地给标准函数设置调用频率上限:超限的调用不会发出请求。

javascript
exchange.IO("rate" | "quota", 名字, 次数, 窗口[, "delay"])

两种模式

  • rate(令牌桶):桶容量默认等于次数,开始时是满的,之后按「次数/窗口」的速度匀速补充,每次调用消耗一个。允许短时间连续调用,长期平均不超过「次数/窗口」。次数写成"10/5"时表示每个窗口补充10次、桶容量为5,用来限制突发。
  • quota(固定窗口):每个窗口内最多调用次数次,进入下一个窗口时清零。窗口按时间纪元对齐:"1s"对齐整秒,"1m"对齐整分钟,"1h"对齐整点,"1d"对齐UTC零点(即北京时间08:00)。例如12:00:00.900开始计数,到12:00:01.000就进入了新窗口。

需要严格保证「任意一个交易所计数周期内不超过N次」时用quota并让窗口与交易所的计数周期一致;只需要控制平均频率时用rate。

参数

参数说明
名字要限制的函数名,见下表。多个名字用逗号分隔(如"GetTicker,GetDepth")时共用一条规则,调用次数合并计算。"*"是兜底规则,只对没有专属规则的函数生效。
次数每个窗口允许的调用次数,必须大于0;rate模式可以写成"次数/突发"。传0或负数表示删除该名字的规则。
窗口时长,写法同Go语言的time.ParseDuration:单位ns、us(或µs)、ms、s、m、h,可以带小数("1.5s"),可以组合("1h30m");另外支持"Nd"表示N天(可以带小数,如"0.5d",不能与其它单位组合)。写成"@HHMM"或"@HHMMSS"(如"@0800")表示按天计数、每天在该时刻(北京时间)清零,rate和quota都可以使用。
动作省略时超限的调用立即失败;写"delay"时阻塞等待,直到有可用次数再发出请求。等待期间停止实盘会打断等待。

可以限制的函数名

类别名字
行情GetTicker、GetTickers、GetDepth、GetTrades、GetRecords、GetMarkets、GetFundings
账户GetAccount、GetAssets、GetPositions、SetMarginLevel
交易CreateOrder(Buy、Sell也计入)、CancelOrder、ModifyOrder
订单查询GetOrder、GetOrders、GetHistoryOrders
条件单CreateConditionOrder、ModifyConditionOrder、CancelConditionOrder、GetConditionOrder、GetConditionOrders、GetHistoryConditionOrders
自定义请求IO/api:只限制exchange.IO("api", ...),不影响其它exchange.IO()指令
  • GetAccount和GetAssets是同一个底层请求,写其中任何一个名字的规则对两个函数都生效。
  • 通过exchange.Go()并发调用时,按实际调用的函数计数。

规则的作用范围

  • 规则按交易所对象分别设置:exchanges[0]上的规则不影响exchanges[1]。
  • 规则只在本次运行中有效,实盘重启后需要重新设置,通常写在main()开头。
  • 同一个名字再次设置时覆盖原规则;名字传空字符串(exchange.IO("rate", ""))清空该交易所对象上的全部规则。
  • 一次调用只按一条规则计数:有专属规则的函数不再计入"*",所以"*"不能当作「所有调用的总配额」叠加在专属规则之上。

超限时的表现

默认动作下,超限的调用不发请求,按调用失败处理(JavaScript返回null,Python返回None,Rust返回Err),错误信息形如:

rate limit exceeded: GetTicker 10/1s quota limit exceeded: GetTicker 10/1m quota limit exceeded: GetRecords 2000/day (resets at 0800)

使用"delay"动作时调用会阻塞到有可用次数,日志中记录的调用时间是等待结束之后的时间。对每天清零的规则使用"delay",最长可能等待到第二天,请谨慎使用。

示例

示例

  • 默认动作:超限时调用失败

    javascript
    function main() { // GetTicker 平均每秒最多 5 次(令牌桶,容量 5) exchange.IO("rate", "GetTicker", 5, "1s") for (var i = 0; i < 10; i++) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { Log("第", i + 1, "次成功:", ticker.Last) } else { // 超限的调用不发请求,返回 null Log("第", i + 1, "次被限流:", GetLastError()) } } }
    python
    def main(): # GetTicker 平均每秒最多 5 次(令牌桶,容量 5) exchange.IO("rate", "GetTicker", 5, "1s") for i in range(10): ticker = exchange.GetTicker("BTC_USDT") if ticker: Log("第", i + 1, "次成功:", ticker["Last"]) else: # 超限的调用不发请求,返回 None Log("第", i + 1, "次被限流:", GetLastError())
    rust
    fn main() { // GetTicker 平均每秒最多 5 次(令牌桶,容量 5) let _ = exchange.IO(("rate", "GetTicker", 5, "1s")); for i in 0..10 { match exchange.GetTicker("BTC_USDT") { Ok(ticker) => Log!("第", i + 1, "次成功:", ticker.Last), // 超限的调用不发请求,返回 Err Err(e) => Log!("第", i + 1, "次被限流:", e), } } }
  • 按交易所的限频规则分组设置

    行情和交易分别共用一条规则;交易类超限时等待而不是失败;其余没有专属规则的函数用"*"兜底。

    javascript
    function main() { // 行情:GetTicker、GetDepth 合计平均每秒 20 次 exchange.IO("rate", "GetTicker,GetDepth", 20, "1s") // 交易:下单(含 Buy/Sell)、撤单合计每秒 5 次,超限时等待 exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s", "delay") // 兜底:其它函数(如 GetAccount、GetPositions)合计每分钟 60 次,窗口对齐整分钟 exchange.IO("quota", "*", 60, "1m") while (true) { var ticker = exchange.GetTicker("BTC_USDT") var depth = exchange.GetDepth("BTC_USDT") if (ticker && depth) { Log("最新价:", ticker.Last, "买一:", depth.Bids[0].Price) } Sleep(1000) } }
    python
    def main(): # 行情:GetTicker、GetDepth 合计平均每秒 20 次 exchange.IO("rate", "GetTicker,GetDepth", 20, "1s") # 交易:下单(含 Buy/Sell)、撤单合计每秒 5 次,超限时等待 exchange.IO("rate", "CreateOrder,CancelOrder", 5, "1s", "delay") # 兜底:其它函数(如 GetAccount、GetPositions)合计每分钟 60 次,窗口对齐整分钟 exchange.IO("quota", "*", 60, "1m") while True: ticker = exchange.GetTicker("BTC_USDT") depth = exchange.GetDepth("BTC_USDT") if ticker and depth: Log("最新价:", ticker["Last"], "买一:", depth["Bids"][0]["Price"]) Sleep(1000)
    rust
    fn main() { // 行情:GetTicker、GetDepth 合计平均每秒 20 次 let _ = exchange.IO(("rate", "GetTicker,GetDepth", 20, "1s")); // 交易:下单(含 Buy/Sell)、撤单合计每秒 5 次,超限时等待 let _ = exchange.IO(("rate", "CreateOrder,CancelOrder", 5, "1s", "delay")); // 兜底:其它函数(如 GetAccount、GetPositions)合计每分钟 60 次,窗口对齐整分钟 let _ = exchange.IO(("quota", "*", 60, "1m")); loop { if let (Ok(ticker), Ok(depth)) = (exchange.GetTicker("BTC_USDT"), exchange.GetDepth("BTC_USDT")) { Log!("最新价:", ticker.Last, "买一:", depth.Bids[0].Price); } Sleep(1000); } }
  • 突发容量、每日配额与删除规则

    javascript
    function main() { // 平均每秒 10 次,但最多连续突发 2 次 exchange.IO("rate", "GetDepth", "10/2", "1s") // 每天北京时间 08:00 清零,每天最多 2000 次 exchange.IO("quota", "GetRecords", 2000, "@0800") // 窗口可以组合单位:每 1 小时 30 分钟最多 100 次 exchange.IO("rate", "GetOrders", 100, "1h30m") // 次数传 0:删除 GetOrders 的规则 exchange.IO("rate", "GetOrders", 0) // 名字传空字符串:清空本交易所对象上的全部规则 exchange.IO("rate", "") }
    python
    def main(): # 平均每秒 10 次,但最多连续突发 2 次 exchange.IO("rate", "GetDepth", "10/2", "1s") # 每天北京时间 08:00 清零,每天最多 2000 次 exchange.IO("quota", "GetRecords", 2000, "@0800") # 窗口可以组合单位:每 1 小时 30 分钟最多 100 次 exchange.IO("rate", "GetOrders", 100, "1h30m") # 次数传 0:删除 GetOrders 的规则 exchange.IO("rate", "GetOrders", 0) # 名字传空字符串:清空本交易所对象上的全部规则 exchange.IO("rate", "")
    rust
    fn main() { // 平均每秒 10 次,但最多连续突发 2 次 let _ = exchange.IO(("rate", "GetDepth", "10/2", "1s")); // 每天北京时间 08:00 清零,每天最多 2000 次 let _ = exchange.IO(("quota", "GetRecords", 2000, "@0800")); // 窗口可以组合单位:每 1 小时 30 分钟最多 100 次 let _ = exchange.IO(("rate", "GetOrders", 100, "1h30m")); // 次数传 0:删除 GetOrders 的规则 let _ = exchange.IO(("rate", "GetOrders", 0)); // 名字传空字符串:清空本交易所对象上的全部规则 let _ = exchange.IO(("rate", "")); }

参考