输入/搜索内容
欢迎使用优宽量化交易平台
编程语言
JavaScript
TypeScript
Python
C++
My语言(麦语言)
PINE语言
Blockly可视化
Workflow工作流
支持的协议
密钥安全性
实盘
策略库
托管者
部署托管者
一键租用托管者
手动部署托管者
托管者操作注意事项
全局指定IP地址
命令行版本托管者程序的参数
实盘数据迁移
托管者监控
交易所
策略编辑器
回测系统
策略入口函数
策略框架与API函数
模板类库
策略参数
交互控件
商品期货
期权交易
股票证券
C++策略编写说明
JavaScript策略编写说明
内置库
扩展API接口
MCP 服务
交易终端
数据探索
Alpha因子分析工具
调试工具
远程编辑
完整策略的导入与导出
多语言支持
实盘、策略分组
实盘展示
策略分享与出租
实盘消息推送
实盘报错、异常退出的常见原因
交易所特殊说明、兼容记录

JavaScriptPythonC++语言编写的策略中,需要在策略主循环中调用Sleep()函数。回测时用于控制回测的速度,实盘时用于控制策略轮询的时间间隔,从而控制访问交易所API接口的请求频率。

示例

  • 经典的商品期货策略架构:

    javascript
    function main(){ while(true){ // 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数 if(exchange.IO("status")){ exchange.SetContractType("MA000") var ticker = exchange.GetTicker() Log("MA000 ticker:", ticker) LogStatus(_D(), "已经连接CTP !") } else { LogStatus(_D(), "未连接CTP !") } } }
    python
    def main(): while True: if exchange.IO("status"): exchange.SetContractType("MA000") ticker = exchange.GetTicker() Log("MA000 ticker:", ticker) LogStatus(_D(), "已经连接CTP !") else: LogStatus(_D(), "未连接CTP !")
    c++
    void main() { while(true) { if(exchange.IO("status") == 1) { exchange.SetContractType("MA000"); auto ticker = exchange.GetTicker(); Log("MA000 ticker:", ticker); LogStatus(_D(), "已经连接CTP !"); } else { LogStatus(_D(), "未连接CTP !"); } } }
  • 设计一个On Bar架构的策略

    javascript
    function onTick() { Log("K线更新,新BAR产生") } function main() { var lastTs = 0 while (true) { if (exchange.IO("status")) { exchange.SetContractType("rb888") var r = _C(exchange.GetRecords) if (r.length > 0 && r[r.length - 1].Time != lastTs) { onTick() lastTs = r[r.length - 1].Time } } Sleep(1000) } }
    python
    def onTick(): Log("K线更新,新BAR产生") def main(): lastTs = 0 while True: if exchange.IO("status"): exchange.SetContractType("rb888") 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线更新,新BAR产生"); } void main() { Record lastBar; lastBar.Time = 0; while (true) { if (exchange.IO("status") == 1) { exchange.SetContractType("rb888"); 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手册

函数名称简介
Version返回系统当前版本号
Sleep休眠函数,参数为暂停的毫秒数
IsVirtual判断执行环境,返回真值表示当前为回测环境
Mail发送邮件
Mail_GoMail函数的异步版本
SetErrorFilter过滤错误日志,参数为正则表达式字符串,匹配该正则表达式的错误日志将不会上传到日志系统
GetPid获取实盘进程ID
GetLastError获取最近一次错误信息
GetCommand获取策略交互命令,策略交互控件设置请参阅:交互控件
GetMeta获取生成策略注册码时写入的Meta值
Dial用于原始Socket访问
HttpQuery发送HTTP请求
HttpQuery_GoHttpQuery函数的异步版本
Encode数据编码函数
UnixNano获取纳秒级时间戳
Unix获取秒级时间戳
GetOS获取系统信息
MD5计算MD5值
DBExec数据库函数,用于执行SQL语句进行数据库操作
UUID生成UUID
EventLoop监听事件
_G持久化保存数据,该函数实现可保存的全局字典功能。数据结构为KV表,永久保存在托管者本地数据库文件中
_D时间戳处理函数,将毫秒时间戳或Date对象转换为时间字符串
_N格式化浮点数,例如_N(3.1415, 2)将截取3.1415小数点后两位,函数返回3.14
_C重试函数,用于接口容错。注意:例如对exchange.GetTicker函数进行容错,应使用_C(exchange.GetTicker)而非_C(exchange.GetTicker())
_Cross交叉判断函数,_Cross()函数返回正数表示上穿周期数,负数表示下穿周期数,0表示当前价格相同
JSONParse解析JSON,可正确解析包含大数值的JSON字符串,会将大数值解析为字符串类型。回测系统不支持JSONParse()函数
SetChannelData在频道上发布最新状态数据,用于实盘间通信
GetChannelData订阅指定实盘的频道数据,用于实盘间通信

函数名称简介
Log输出日志,支持设置日志文本颜色、推送功能,以及打印base64编码的图片
LogProfit输出盈亏数据,打印盈亏数值并根据数值绘制收益曲线
LogProfitReset清空LogProfit函数输出的所有收益日志和收益图表
LogStatus在状态栏输出信息,支持在状态栏中设置按钮控件和输出表格
EnableLog开启或关闭订单信息的日志记录功能
Chart图表绘制函数,基于Highcharts/Highstocks图表库
KLineChartPine语言风格的图表绘制函数,用于在策略运行时以类似Pine语言的方式进行自定义绘图
LogReset清除日志,支持通过参数设置保留最近指定数量的日志记录
LogVacuum回收SQLite资源,在调用LogReset()函数清除日志后,回收SQLite删除数据时占用的存储空间
console.log在实盘页面的「调试信息」栏中输出调试信息
console.error在实盘页面的「调试信息」栏中输出错误信息

函数名称简介
exchange.GetTicker获取Tick行情数据
exchange.GetDepth获取订单簿深度数据
exchange.GetTrades获取市场成交记录(基于Tick数据计算)
exchange.GetRecords获取K线数据
exchange.GetPeriod获取当前K线周期
exchange.SetMaxBarLen设置K线最大长度
exchange.GetRate获取当前设置的汇率
exchange.SetData设置策略运行时加载的数据
exchange.GetData获取已加载的数据或外部链接提供的数据
exchange.GetMarkets获取已订阅的交易品种信息
exchange.GetTickers获取已订阅交易品种的聚合行情数据

函数名称简介
exchange.Buy提交买单,期货合约下单时必须注意交易方向是否设置正确,如果交易方向与交易函数不匹配将报错
exchange.Sell提交卖单,期货合约下单时必须注意交易方向是否设置正确,如果交易方向与交易函数不匹配将报错
exchange.CreateOrder提交订单,通过参数指定交易品种、交易方向、价格和数量
exchange.CancelOrder取消订单
exchange.GetOrder获取订单信息,数据结构为Order结构
exchange.GetOrders获取未完成的订单,数据结构为Order结构数组(列表)
exchange.GetHistoryOrders获取当前交易日所有合约的历史订单;支持获取指定合约的当前交易日历史订单
exchange.SetPrecision设置exchange交易所对象的价格与下单量精度,设置后系统将自动忽略超出精度的部分
exchange.SetRate设置汇率
exchange.IO用于交易所对象相关的其他接口调用
exchange.Log输出、记录交易日志,不执行实际下单
exchange.GetName获取交易所对象的名称
exchange.GetLabel获取交易所对象的标签
exchange.GetCurrency获取当前交易对
exchange.GetAccount获取账户信息
exchange.GetQuoteCurrency获取当前交易对的计价币名称

函数名称简介
exchange.GetPositions获取期货持仓信息,返回Position结构数组(列表)
exchange.SetMarginLevel设置杠杆倍数
exchange.SetDirection设置exchange.Buy函数、exchange.Sell函数在期货合约下单时的交易方向
exchange.SetContractType设置合约代码,例如切换当前操作的合约为螺纹钢主力连续合约:exchange.SetContractType("rb888")
exchange.GetContractType获取当前设置的合约代码
StrDecodeGBK编码解码

功能概述

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

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

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

基础用法

函数签名

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

参数说明

参数类型必填说明
modestring限流模式:"rate"(平滑限流)或 "quota"(额度限流)
functionNamesstring要限制的函数名,支持单个函数、多个函数(逗号分隔)、通配符(*表示所有函数)
maxCallsnumber时间周期内允许的最大调用次数
periodstring时间周期或重置时间点。支持时间单位:ns、us、µs、ms、s、m、h、d(例如:"1s"、"1m"、"1h")。支持重置时间点:@HHMM 或 @HHMMSS(例如:"@0815"表示每天08:15重置)
behaviorstring超限行为。默认为空(超限返回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", ...)调用生效

函数名称配置

单个函数限流

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

示例

  • undefined
    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++
    // 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++
    // C++暂不支持
  • 通配符限流

    使用通配符*限制所有API调用:

    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++
    // C++暂不支持
  • 时间周期配置

    固定时间周期

    支持多种时间单位:ns(纳秒)、us/µs(微秒)、ms(毫秒)、s(秒)、m(分钟)、h(小时)、d(天)。

    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++
    // C++暂不支持
  • 日内重置时间点

    使用@HHMM@HHMMSS格式指定每日的重置时间:

    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++
    // C++暂不支持
  • 行为模式配置

    默认模式(返回 null)

    超限时 API 调用返回 null,不等待:

    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++
    // C++暂不支持
  • delay模式(自动等待)

    超限时自动等待,直到可以调用:

    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++
    // C++暂不支持
  • 限流模式对比

    rate模式 vs quota模式

    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++
    // C++暂不支持
  • 实际应用场景

    场景1:单品种行情监控

    监控单个合约的行情,避免频繁调用:

    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++
    // C++暂不支持
  • 场景2:多品种轮询策略

    轮询多个合约行情,控制总体调用频率:

    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++
    // C++暂不支持
  • 场景3:高频交易策略

    对交易相关接口进行严格限流:

    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++
    // C++暂不支持
  • 场景4:日内交易次数控制

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

    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++
    // C++暂不支持
  • 注意事项

    • 必须检查连接状态

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

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

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

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

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

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

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

    最佳实践

    • 分级限流策略

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

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

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

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

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

功能概述

策略实盘间通信功能允许不同的实盘策略之间进行数据共享和状态同步。通过频道机制,一个实盘可以将自己的状态数据广播给其他实盘,实现跨实盘、跨托管者、跨服务器的数据通信。

核心概念

  • 频道(Channel):每个实盘都有一个独立的频道,频道ID即为实盘ID
  • 广播端:使用SetChannelData()函数在频道上发布数据的实盘
  • 订阅端:使用GetChannelData()函数订阅其他实盘频道数据的实盘
  • 状态覆盖:频道上仅保存最新状态,新数据会覆盖旧数据,而非消息队列

主要特性

  • 非阻塞通信:所有函数调用均为非阻塞式,不会影响策略主流程
  • 跨平台支持:支持跨实盘、跨托管者、跨服务器进行数据传输
  • 多频道订阅:单个实盘可同时订阅多个不同实盘的频道
  • 灵活的数据格式:支持任意可JSON序列化的数据结构

应用场景

  • 主从策略协同:主策略分析市场并广播信号,从策略接收信号执行交易
  • 多账户同步:在多个交易账户之间同步交易信号和仓位信息
  • 策略监控:监控策略广播运行状态,监控实盘订阅并进行展示或告警
  • 数据共享:共享行情分析、指标计算等结果,避免重复计算

基本用法

示例

  • 广播端示例 - 发布市场数据

    javascript
    function main() { var updateId = 0 var robotId = _G() // 获取当前实盘ID while(true) { if (!exchange.IO("status")) { Sleep(1000) continue } // 获取市场数据 var ticker = exchange.GetTicker("rb888") if (!ticker) { Sleep(5000) continue } // 准备频道状态数据 var channelState = { robotId: robotId, updateId: ++updateId, timestamp: Date.now(), symbol: "rb888", lastPrice: ticker.Last, volume: ticker.Volume, high: ticker.High, low: ticker.Low } // 在频道上发布最新状态(覆盖旧状态) SetChannelData(channelState) // 显示当前频道状态 LogStatus("频道广播端 [实盘ID: " + robotId + "]\n" + "更新ID: #" + channelState.updateId + "\n" + "时间: " + _D(channelState.timestamp) + "\n" + "交易对: " + channelState.symbol + "\n" + "最新价: $" + channelState.lastPrice.toFixed(2)) Sleep(60000) // 每分钟更新一次频道状态 } }
    python
    def main(): updateId = 0 robotId = _G() # 获取当前实盘ID while True: if not exchange.IO("status"): Sleep(1000) continue # 获取市场数据 ticker = exchange.GetTicker("rb888") if not ticker: Sleep(5000) continue # 准备频道状态数据 channelState = { "robotId": robotId, "updateId": updateId + 1, "timestamp": time.time() * 1000, "symbol": "rb888", "lastPrice": ticker["Last"], "volume": ticker["Volume"], "high": ticker["High"], "low": ticker["Low"] } updateId += 1 # 在频道上发布最新状态(覆盖旧状态) SetChannelData(channelState) # 显示当前频道状态 LogStatus("频道广播端 [实盘ID: {}]\n".format(robotId) + "更新ID: #{}\n".format(channelState["updateId"]) + "时间: {}\n".format(_D(channelState["timestamp"])) + "最新价: ${:.2f}".format(channelState["lastPrice"])) Sleep(60000) # 每分钟更新一次频道状态
    c++
  • 订阅端示例 - 订阅多个频道

    javascript
    function main() { // 需要订阅的两个频道ID(根据实际情况修改) var channelId1 = "632799" // 频道1的实盘ID var channelId2 = "632800" // 频道2的实盘ID while(true) { // 订阅频道1的当前状态 var state1 = GetChannelData(channelId1) // 订阅频道2的当前状态 var state2 = GetChannelData(channelId2) // 构建状态显示 var statusMsg = "频道订阅端 - 当前订阅状态\n\n" // 显示频道1状态 statusMsg += "═══ 频道1 [" + channelId1 + "] ═══\n" if (state1 !== null) { statusMsg += "更新ID: #" + state1.updateId + "\n" statusMsg += "时间: " + _D(state1.timestamp) + "\n" statusMsg += "交易对: " + state1.symbol + "\n" statusMsg += "最新价: $" + state1.lastPrice.toFixed(2) + "\n" } else { statusMsg += "状态: 等待中...(首次调用返回 null)\n" } statusMsg += "\n" // 显示频道2状态 statusMsg += "═══ 频道2 [" + channelId2 + "] ═══\n" if (state2 !== null) { statusMsg += "更新ID: #" + state2.updateId + "\n" statusMsg += "时间: " + _D(state2.timestamp) + "\n" statusMsg += "最新价: $" + state2.lastPrice.toFixed(2) + "\n" } else { statusMsg += "状态: 等待中...(首次调用返回 null)\n" } LogStatus(statusMsg) Sleep(5000) // 每5秒订阅一次频道 } }
    python
    def main(): # 需要订阅的两个频道ID(根据实际情况修改) channelId1 = "632799" # 频道1的实盘ID channelId2 = "632800" # 频道2的实盘ID while True: # 订阅频道1的当前状态 state1 = GetChannelData(channelId1) # 订阅频道2的当前状态 state2 = GetChannelData(channelId2) # 构建状态显示 statusMsg = "频道订阅端 - 当前订阅状态\n\n" # 显示频道1状态 statusMsg += "═══ 频道1 [{}] ═══\n".format(channelId1) if state1 is not None: statusMsg += "更新ID: #{}\n".format(state1["updateId"]) statusMsg += "时间: {}\n".format(_D(state1["timestamp"])) statusMsg += "最新价: ${:.2f}\n".format(state1["lastPrice"]) else: statusMsg += "状态: 等待中...(首次调用返回 None)\n" statusMsg += "\n" # 显示频道2状态 statusMsg += "═══ 频道2 [{}] ═══\n".format(channelId2) if state2 is not None: statusMsg += "更新ID: #{}\n".format(state2["updateId"]) statusMsg += "时间: {}\n".format(_D(state2["timestamp"])) statusMsg += "最新价: ${:.2f}\n".format(state2["lastPrice"]) else: statusMsg += "状态: 等待中...(首次调用返回 None)\n" LogStatus(statusMsg) Sleep(5000) # 每5秒订阅一次频道
    c++
  • 实际应用场景

    场景1:主从策略协同交易

    主策略(信号广播端)

    javascript
    function main() { var robotId = _G() Log("主策略启动,实盘ID:", robotId) while(true) { if (!exchange.IO("status")) { Sleep(1000) continue } // 分析市场,生成交易信号 var records = exchange.GetRecords("rb888") if (!records || records.length < 20) { Sleep(5000) continue } // 简单的均线策略 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" } // 广播交易信号 var signalData = { timestamp: Date.now(), symbol: "rb888", signal: signal, price: records[records.length-1].Close, ma5: ma5[ma5.length-1], ma20: ma20[ma20.length-1] } SetChannelData(signalData) LogStatus("主策略 - 信号广播\n" + "信号: " + signal + "\n" + "价格: $" + signalData.price.toFixed(2) + "\n" + "MA5: " + signalData.ma5.toFixed(2) + "\n" + "MA20: " + signalData.ma20.toFixed(2)) Sleep(60000) } }
    python
    def main(): robotId = _G() Log("主策略启动,实盘ID:", robotId) while True: if not exchange.IO("status"): Sleep(1000) continue # 分析市场,生成交易信号 records = exchange.GetRecords("rb888") if not records or len(records) < 20: Sleep(5000) continue # 简单的均线策略 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" # 广播交易信号 signalData = { "timestamp": time.time() * 1000, "symbol": "rb888", "signal": signal, "price": records[-1]["Close"], "ma5": ma5[-1], "ma20": ma20[-1] } SetChannelData(signalData) LogStatus("主策略 - 信号广播\n" + "信号: {}\n".format(signal) + "价格: ${:.2f}\n".format(signalData["price"]) + "MA5: {:.2f}\n".format(signalData["ma5"]) + "MA20: {:.2f}".format(signalData["ma20"])) Sleep(60000)
    c++
  • 实际应用场景

    场景1:主从策略协同交易

    从策略(信号接收执行端)

    javascript
    function main() { var masterRobotId = "632799" // 主策略的实盘ID var lastSignal = null Log("从策略启动,订阅主策略:", masterRobotId) while(true) { // 获取主策略的信号 var signalData = GetChannelData(masterRobotId) if (signalData === null) { LogStatus("等待主策略信号...") Sleep(5000) continue } // 检查是否有新信号 if (lastSignal !== signalData.signal) { Log("收到新信号:", signalData.signal, "价格:", signalData.price) // 执行交易 if (signalData.signal === "BUY") { var ticker = exchange.GetTicker(signalData.symbol) if (ticker) { exchange.Buy(ticker.Last, 0.01) Log("执行买入,价格:", ticker.Last) } } else if (signalData.signal === "SELL") { var ticker = exchange.GetTicker(signalData.symbol) if (ticker) { exchange.Sell(ticker.Last, 0.01) Log("执行卖出,价格:", ticker.Last) } } lastSignal = signalData.signal } LogStatus("从策略 - 跟随主策略\n" + "当前信号: " + signalData.signal + "\n" + "信号价格: $" + signalData.price.toFixed(2) + "\n" + "信号时间: " + _D(signalData.timestamp)) Sleep(5000) } }
    python
    def main(): masterRobotId = "632799" # 主策略的实盘ID lastSignal = None Log("从策略启动,订阅主策略:", masterRobotId) while True: # 获取主策略的信号 signalData = GetChannelData(masterRobotId) if signalData is None: LogStatus("等待主策略信号...") Sleep(5000) continue # 检查是否有新信号 if lastSignal != signalData["signal"]: Log("收到新信号:", signalData["signal"], "价格:", signalData["price"]) # 执行交易 if signalData["signal"] == "BUY": ticker = exchange.GetTicker(signalData["symbol"]) if ticker: exchange.Buy(ticker["Last"], 0.01) Log("执行买入,价格:", ticker["Last"]) elif signalData["signal"] == "SELL": ticker = exchange.GetTicker(signalData["symbol"]) if ticker: exchange.Sell(ticker["Last"], 0.01) Log("执行卖出,价格:", ticker["Last"]) lastSignal = signalData["signal"] LogStatus("从策略 - 跟随主策略\n" + "当前信号: {}\n".format(signalData["signal"]) + "信号价格: ${:.2f}\n".format(signalData["price"]) + "信号时间: {}".format(_D(signalData["timestamp"]))) Sleep(5000)
    c++
  • 场景2:多策略状态监控

    监控策略

    javascript
    function main() { // 需要监控的策略实盘ID列表 var monitorList = ["632799", "632800", "632801"] while(true) { var table = { type: "table", title: "策略运行状态监控", cols: ["实盘ID", "状态", "最后更新", "交易对", "当前价格", "盈亏"], 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 ? "运行中" : "异常" 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, "等待数据", "-", "-", "-", "-" ]) } } LogStatus("`" + JSON.stringify(table) + "`") Sleep(10000) } }
    python
    def main(): # 需要监控的策略实盘ID列表 monitorList = ["632799", "632800", "632801"] while True: table = { "type": "table", "title": "策略运行状态监控", "cols": ["实盘ID", "状态", "最后更新", "交易对", "当前价格", "盈亏"], "rows": [] } for robotId in monitorList: data = GetChannelData(robotId) if data is not None: updateTime = _D(data["timestamp"]) timeDiff = time.time() * 1000 - data["timestamp"] status = "运行中" if timeDiff < 120000 else "异常" 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, "等待数据", "-", "-", "-", "-" ]) LogStatus("`" + json.dumps(table) + "`") Sleep(10000)
    c++
  • API函数说明

    SetChannelData(data)

    功能:在频道上发布最新状态数据

    参数

    • data:需要发布的数据,可以是任何可JSON序列化的数据结构

    返回值:无

    特性

    • 非阻塞调用
    • 覆盖之前的数据,不累积历史
    • 自动使用当前实盘ID作为频道ID

    数据长度限制

    • JSON序列化后不超过1024字节
    • 建议仅传输必要的状态信息

    详细文档SetChannelData

    GetChannelData(robotId)

    功能:订阅指定实盘的频道数据

    参数

    • robotId:要订阅的实盘ID(字符串或数字)

    返回值

    • 首次调用返回null,需要重试
    • 成功后返回频道的最新数据

    特性

    • 非阻塞调用
    • 可订阅多个频道
    • 可订阅自己的频道

    详细文档GetChannelData

    注意事项

    • 首次调用返回nullGetChannelData()函数首次调用时会返回null,这是正常现象,需要等待数据同步完成。建议在代码中进行null判断。

    • 数据覆盖机制:频道上仅保存最新状态,调用SetChannelData()会覆盖之前的数据。如需保存历史数据,应在订阅端自行记录。

    • 非阻塞特性:所有频道通信函数均为非阻塞的,不会影响策略的主流程执行。但这也意味着无法保证数据的即时性。

    • 数据大小限制:传入SetChannelData的数据在JSON序列化后不得超过1024字节。应仅传输必要的状态信息,如交易信号、价格、持仓等关键数据,避免传输完整的K线数组或大量历史数据。

    • 实盘环境限制:频道通信功能主要适用于实盘环境,在回测系统中可能受限或不可用。

    • 实盘ID获取:可通过_G()函数获取当前实盘ID,也可在平台界面查看实盘ID。

    • 安全性考虑:频道数据可能被其他有权限的实盘订阅,请勿在频道中传输敏感信息(如API密钥等)。

    最佳实践

    • 合理的更新频率:根据实际需求设置数据更新频率,避免过于频繁的更新造成资源浪费。

    • 数据结构设计:设计清晰的数据结构,包含必要的元数据(如时间戳、版本号等),便于订阅端处理。

    • 错误处理:订阅端应处理null返回值,广播端应确保数据格式正确。

    • 状态版本控制:在数据中包含版本号或更新ID,帮助订阅端判断是否有新数据。

    • 监控与告警:对于关键的通信链路,建议实现超时监控和告警机制。

    • 测试验证:在正式使用前,先在测试环境验证频道通信的稳定性和延迟。

    • 文档记录:记录频道数据格式和通信协议,便于后续维护和多人协作。

参考

优宽量化交易平台从系统底层真正支持JavaScript语言策略的多线程功能,实现了以下对象:

对象说明备注
threading多线程全局对象成员函数:ThreadgetThreadmainThread等。
Thread线程对象成员函数:peekMessagepostMessagejoin等。
ThreadLock线程锁对象成员函数:acquirerelease。可作为线程执行函数的参数传入线程环境。
ThreadEvent事件对象成员函数:setclearwaitisSet。可作为线程执行函数的参数传入线程环境。
ThreadCondition条件对象成员函数:notifynotifyAllwaitacquirerelease。可作为线程执行函数的参数传入线程环境。
ThreadDict字典对象成员函数:getset。可作为线程执行函数的参数传入线程环境。

优宽量化交易平台语法手册:JavaScript多线程

函数名称简介
TA.MACD计算指数平滑异同移动平均线指标
TA.KDJ计算随机指标
TA.RSI计算相对强弱指标
TA.ATR计算真实波动幅度均值指标
TA.OBV计算能量潮指标
TA.MA计算移动平均线指标
TA.EMA计算指数移动平均线指标
TA.BOLL计算布林带指标
TA.Alligator计算鳄鱼线指标
TA.CMF计算蔡金资金流量指标
TA.Highest计算指定周期内的最高价
TA.Lowest计算指定周期内的最低价
TA.SMA计算简单移动平均线指标

talib指标库包含众多技术分析指标,例如:talib.CDL2CROWS。详细信息请参阅语法手册。