exchange.IO
exchange.IO()函数用于调用交易所对象相关的协议和其他接口。
exchange.IO(k, ...args)示例
-
使用status参数判断与期货公司前置机的连接状态:
exchange.IO("status"),返回true表示与CTP服务器行情和交易两台服务器均连接正常。javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) } }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D())c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); } } -
使用
wait参数设置阻塞模式:exchange.IO("wait", Timeout),当前交易所任何品种更新行情信息或订单成交时才返回,可带第二个参数(毫秒数)指定超时时间,超时返回空值,正常返回EventTick/OrderEvent结构。结合exchange.IO("mode", 0)函数使用,这样配合使用可以使程序在有最新行情时进行响应,执行程序逻辑(使用exchange.IO("mode", 0)不影响exchange.IO("wait"),目的是为了在程序中使用exchange.GetTicker()等函数调用时不阻塞)。如果Timeout参数设置为-1,该函数设置为立即返回,在没有新事件时返回空值;Timeout参数设置为0表示阻塞等待最新事件,如同不设置Timeout参数。需要注意的是在使用exchange.IO("wait")时,必须至少已经订阅了一个当前处于交易状态的合约(已经交割的过期合约不会再有行情数据),否则会阻塞在该函数(由于没有任何行情、订单更新)。仅支持商品期货实盘。EventTick:{Event:"tick", Index:交易所索引, Nano:事件纳秒级时间, Symbol:合约名称, Ticker:行情数据}。OrderTick:{Event:"order", Index:交易所索引, Nano:事件纳秒级时间, Order:订单信息}。简单实现回调机制:
javascriptfunction on_tick(symbol, ticker) { Log("symbol:", symbol, "update") Log("ticker:", ticker) } function on_order(order) { Log("order update", order) } function main() { while(!exchange.IO("status")) { Sleep(10) } // 如果在程序中不使用诸如exchange.GetTicker()之类的获取行情的函数,可以不设置exchange.IO("mode", 0) exchange.IO("mode", 0) _C(exchange.SetContractType, "MA005") while(true) { var e = exchange.IO("wait") if(e) { if(e.Event == "tick") { on_tick(e.Symbol, e.Ticker) } else if(e.Event == "order") { on_order(e.Order) } } } }pythondef on_tick(symbol, ticker): Log("symbol:", symbol, "update") # 数据结构:https://www.youquant.com/api#ticker Log("ticker:", ticker) def on_order(order): Log("order update", order) def main(): # wait connect trade server while not exchange.IO("status"): Sleep(10) # switch push mode exchange.IO("mode", 0) # subscribe instrument _C(exchange.SetContractType, "MA001") while True: e = exchange.IO("wait") if e: if e.Event == "tick": on_tick(e['Symbol'], e['Ticker']) elif e.Event == "order": on_order(e['Order'])c++void on_tick(const string &symbol, json &ticker) { Log("symbol:", symbol, "update"); Log("ticker:", ticker); } void on_order(json &order) { Log("order update", order); } void main() { while(exchange.IO("status") == 0) { Sleep(10); } exchange.IO("mode", 0); _C(exchange.SetContractType, "rb2005"); while(true) { auto e = exchange.IO("wait"); if(e != false) { if(e["Event"] == "tick") { on_tick(e["Symbol"], e["Ticker"]); } else if(e["Event"] == "order") { on_order(e["Order"]); } } } } -
多品种回调示例:
javascriptfunction on_tick(symbol, ticker) { Log("symbol:", symbol, "update", "ticker:", ticker) } function main() { while(!exchange.IO("status")) { Sleep(10) } _C(exchange.SetContractType, "MA101") _C(exchange.SetContractType, "rb2101") _C(exchange.SetContractType, "i2101") while(true) { var e = exchange.IO("wait", -1) if(e) { if(e.Event == "tick") { on_tick(e.Symbol, e.Ticker) } } Sleep(10) } }pythondef on_tick(symbol, ticker): Log("symbol:", symbol, "update", "ticker:", ticker) def main(): while not exchange.IO("status"): Sleep(10) _C(exchange.SetContractType, "MA101") _C(exchange.SetContractType, "rb2101") _C(exchange.SetContractType, "i2101") while True: e = exchange.IO("wait", -1) if e: if e.Event == "tick": on_tick(e['Symbol'], e['Ticker']) Sleep(10)c++void on_tick(const string &symbol, json &ticker) { Log("symbol:", symbol, "update", "ticker:", ticker); } void main() { while(exchange.IO("status") == 0) { Sleep(10); } _C(exchange.SetContractType, "MA101"); _C(exchange.SetContractType, "rb2101"); _C(exchange.SetContractType, "i2101"); while(true) { auto e = exchange.IO("wait", -1); if(e != false) { if(e["Event"] == "tick") { on_tick(e["Symbol"], e["Ticker"]); } } } } -
使用
instruments参数获取所有合约的列表数据:exchange.IO("instruments"),返回交易所所有合约的列表,仅支持实盘环境。javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) } Log("开始获取所有合约") var instruments = _C(exchange.IO, "instruments") Log("合约列表获取成功") var len = 0 for (var instrumentId in instruments) { len++ } Log("合约列表长度为:",len) }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) Log("开始获取所有合约") instruments = _C(exchange.IO, "instruments") Log("合约列表获取成功") length = 0 for i in range(len(instruments)): length += 1 Log("合约列表长度为:", length)c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); } Log("开始获取所有合约"); auto instruments = _C(exchange.IO, "instruments"); Log("合约列表获取成功"); int length = 0; for(int i = 0; i < instruments.size(); i++) { length++; } Log("合约列表长度为:", length); } -
使用
products参数,获取所有产品的列表数据:exchange.IO("products"),返回交易所所有产品的列表,仅支持实盘。使用
subscribed参数,获取已订阅的合约数据:exchange.IO("subscribed"),返回已订阅行情的合约,仅支持实盘。使用
settlement参数,获取结算单数据:exchange.IO("settlement"),结算单查询,不添加第二个参数时默认返回前一个交易日的数据,添加参数如20170317则返回日期为2017-03-17的结算单,仅支持实盘。使用api参数,调用底层接口:
优宽量化的CTP(商品期货)终端提供了完整的全API实现,当发明者平台的API无法满足您所需的功能时,可以使用
exchange.IO函数进行更深层的系统调用,完全兼容官方的API名称。CTP的IO直接扩展函数调用请求,将在收到第一个isLast标记为true的响应包后返回。CTP协议接口:CTP协议接口相关资料
以几个简单的示例进行说明:
-
查询投资者信息:
javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) } Log(exchange.IO("api", "ReqQryInvestor")) }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) Log(exchange.IO("api", "ReqQryInvestor"))c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); } Log(exchange.IO("api", "ReqQryInvestor")); } -
修改密码:
javascriptfunction main() { // CTP协议建立连接时需要时间 Sleep(6000) exchange.IO("api", "ReqUserPasswordUpdate", {BrokerID: "9999", UserID: "11111", OldPassword: "oldpass", NewPassword: "newpass"}) }pythondef main(): Sleep(6000) exchange.IO("api", "ReqUserPasswordUpdate", {"BrokerID": "9999", "UserID": "11111", "OldPassword": "oldpass", "NewPassword": "newpass"})c++void main() { Sleep(6000); exchange.IO("api", "ReqUserPasswordUpdate", R"({"BrokerID": "9999", "UserID": "11111", "OldPassword": "oldpass", "NewPassword": "newpass"})"_json); } -
复杂示例:
javascriptfunction main() { // CTP协议建立连接时需要时间 Sleep(6000) // 如果再加一个参数值为false表示不等待返回值,只发送请求,第三个参数只需要填充需要的字段,也可省略此参数,如果类型为char,传长度为1的字符串即可 var r = exchange.IO("api", "ReqQryProduct", {ProductID: "MA"}) // CTP未登录的时候会失败 if (!r) { return } _.each(r, function(item) { // IO请求可能返回多个数据包,所以以数组的形式返回。遍历数据包的所有数据类型,一个数据包可能包含多个具体数据,具体数据类型的名称,请参看CTP官方文档http://www.sfit.com.cn/5_2_DocumentDown.htm _.each(item, function(f) { // 取出来需要的数据,Name为此数据的类型,Value为此数据的值 if (f.Name == 'CThostFtdcProductField') { // 打印查询的甲醇信息 Log(f.Value) } }) }); }pythondef main(): Sleep(6000) r = exchange.IO("api", "ReqQryProduct", {"ProductID": "MA"}) if not r: return for r_index in range(len(r)): for f_index in range(len(r[r_index])): if r[r_index][f_index]["Name"] == 'CThostFtdcProductField': Log(r[r_index][f_index]["Value"])c++void main() { Sleep(6000); auto r = exchange.IO("api", "ReqQryProduct", R"({"ProductID": "MA"})"_json); if(r == false) { return; } for(auto& ele1 : r.items()) { for(auto& ele2 : ele1.value().items()) { if(ele2.value()["Name"] == "CThostFtdcProductField") { Log(ele2.value()["Value"]); } } } } -
复杂示例:
javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) } // 也可不指定日期 var r = exchange.IO("api", "ReqQrySettlementInfo", {TradingDay: "20190506"}) var s = '' _.each(r, function(item) { _.each(item, function(f) { if (f.Name == 'CThostFtdcSettlementInfoField') { s += f.Value.Content } }) }) Log(s) }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) r = exchange.IO("api", "ReqQrySettlementInfo", {"TradingDay": "20190506"}) s = '' for i in range(len(r)): for ii in range(len(r[i])): if r[i][ii]["Name"] == "CThostFtdcSettlementInfoField": s += r[i][ii]["Value"]["Content"] Log(s)c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); } auto r = exchange.IO("api", "ReqQrySettlementInfo", R"({"TradingDay": "20200311"})"_json); string s = ""; for(auto& ele1 : r.items()) { for(auto& ele2 : ele1.value().items()) { if(ele2.value()["Name"] == "CThostFtdcSettlementInfoField") { s += std::string(ele2.value()["Value"]["Content"]); } } } Log(s); } -
限制 GetTicker 每秒最多调用 3 次,超出限制时报错并返回空值:
javascriptfunction main() { // 等待与交易服务器连接 while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) Sleep(1000) } // 场景1: 限制GetTicker每秒最多调用3次,超限返回null exchange.IO("rate", "GetTicker", 3, "1s") for (var i = 0; i < 10; i++) { if (!exchange.IO("status")) { Log("Exchange not ready, waiting...", "#FF0000") Sleep(5000) continue } var ticker = exchange.GetTicker("rb888") // 螺纹钢主力合约 if (ticker) { Log("Call", i+1, "Success, Price:", ticker.Last) } else { Log("Call", i+1, "Failed: Rate limit exceeded", "#FF0000") } Sleep(100) } }pythondef main(): # 等待与交易服务器连接 while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) Sleep(1000) # 场景1: 限制GetTicker每秒最多调用3次,超限返回null exchange.IO("rate", "GetTicker", 3, "1s") for i in range(10): if not exchange.IO("status"): Log("Exchange not ready, waiting...", "#FF0000") Sleep(5000) continue ticker = exchange.GetTicker("rb888") # 螺纹钢主力合约 if ticker: Log("Call", i+1, "Success, Price:", ticker["Last"]) else: Log("Call", i+1, "Failed: Rate limit exceeded", "#FF0000") Sleep(100)c++// C++ 暂不支持 -
限制GetTicker每秒最多调用3次,超限时等待:
javascriptfunction main() { while (!exchange.IO("status")) { Sleep(1000) } // 场景2: 使用delay参数,超限时自动等待 exchange.IO("rate", "GetTicker", 3, "1s", "delay") Log("Start testing delay mode with m888 (soybean meal)", "#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() if (ticker) { Log("Call", i+1, "Price:", ticker.Last, "Time cost:", endTime - startTime, "ms") } } }pythonimport time def main(): while not exchange.IO("status"): Sleep(1000) # 场景2: 使用delay参数,超限时自动等待 exchange.IO("rate", "GetTicker", 3, "1s", "delay") Log("Start testing delay mode with m888 (soybean meal)", "#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 if ticker: Log("Call", i+1, "Price:", ticker["Last"], "Time cost:", endTime - startTime, "ms")c++// C++暂不支持 -
限制多个函数联合调用频率:
javascriptfunction main() { while (!exchange.IO("status")) { Sleep(1000) } // 场景3: GetTicker和GetDepth共享限制,合计每秒最多5次 exchange.IO("mode", 0) // 切换为接口立即返回模式 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("i888") // 铁矿石主力合约 Log("Call", i+1, "GetTicker:", ticker ? "Success" : "Failed") } else { var depth = exchange.GetDepth("i888") Log("Call", i+1, "GetDepth:", depth ? "Success" : "Failed") } Sleep(100) } }pythondef main(): while not exchange.IO("status"): Sleep(1000) # 场景3: GetTicker和GetDepth共享限制,合计每秒最多5次 exchange.IO("mode", 0) # 切换为接口立即返回模式 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("i888") # 铁矿石主力合约 Log("Call", i+1, "GetTicker:", "Success" if ticker else "Failed") else: depth = exchange.GetDepth("i888") Log("Call", i+1, "GetDepth:", "Success" if depth else "Failed") Sleep(100)c++// C++暂不支持 -
使用通配符限制所有API调用:
javascriptfunction main() { while (!exchange.IO("status")) { Sleep(1000) } // 场景4: 限制所有API调用每秒最多5次 exchange.IO("mode", 0) // 设置为立即返回模式 exchange.IO("rate", "*", 5, "1s") var symbols = ["rb888", "m888", "i888"] // 螺纹钢、豆粕、铁矿石 for (var i = 0; i < 10; i++) { if (!exchange.IO("status")) { Sleep(5000) continue } var symbol = symbols[i % symbols.length] var ticker = exchange.GetTicker(symbol) var depth = exchange.GetDepth(symbol) var account = exchange.GetAccount() Log("Round", i+1, symbol, "Ticker:", ticker ? "✓" : "✗", "Depth:", depth ? "✓" : "✗", "Account:", account ? "✓" : "✗") Sleep(100) } }pythondef main(): while not exchange.IO("status"): Sleep(1000) # 场景4: 限制所有API调用每秒最多5次 exchange.IO("mode", 0) # 设置为立即返回模式 exchange.IO("rate", "*", 5, "1s") symbols = ["rb888", "m888", "i888"] # 螺纹钢、豆粕、铁矿石 for i in range(10): if not exchange.IO("status"): Sleep(5000) continue symbol = symbols[i % len(symbols)] ticker = exchange.GetTicker(symbol) depth = exchange.GetDepth(symbol) account = exchange.GetAccount() Log("Round", i+1, symbol, "Ticker:", "✓" if ticker else "✗", "Depth:", "✓" if depth else "✗", "Account:", "✓" if account else "✗") Sleep(100)c++// C++暂不支持 -
使用配额模式进行严格时间窗口对齐限流:
javascriptfunction main() { while (!exchange.IO("status")) { Sleep(1000) } // 场景5: quota模式,严格时间窗口对齐 exchange.IO("mode", 0) // 切换到立即返回模式 exchange.IO("quota", "GetTicker", 3, "1s") Log("Testing quota mode with c888 (corn)", "#00FF00") 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一次,一秒内约6-7次 } }pythondef main(): while not exchange.IO("status"): Sleep(1000) # 场景5: quota模式,严格时间窗口对齐 exchange.IO("mode", 0) # 切换到立即返回模式 exchange.IO("quota", "GetTicker", 3, "1s") Log("Testing quota mode with c888 (corn)", "#00FF00") 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一次,一秒内约6-7次c++// C++暂不支持
返回值
| 类型 | 描述 |
string / number / bool / object / array / any (系统支持的所有类型) |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
arg | string / number / bool / object / array / any (系统支持的所有类型) | 是 | 扩展参数,根据具体调用场景传参, |
参考
备注
使用mode参数切换行情模式:
-
exchange.IO("mode", 0)
立即返回模式,如果当前还没有接收到交易所最新的行情数据推送,就立即返回旧的行情数据,如果有新的数据就返回新的数据。
商品期货中的应用,可以参考:exchange.IO函数的wait参数的使用例子。 -
exchange.IO("mode", 1)
缓存模式(默认模式),如果当前还没有收到交易所最新的行情数据(与上一次接口获取的数据比较),就等待接收然后再返回,如果调用该函数之前收到了最新的行情数据,就立即返回最新的数据。 -
exchange.IO("mode", 2)
强制更新模式,进入等待直到接收到交易所下一次的最新推送数据后返回。
API限流控制功能:
优宽量化平台支持对交易所API调用进行频率限制,防止触发交易所的频率限制。支持两种限流模式:
- rate模式(平滑限流):不严格对齐时间窗口,适用于一般限流需求。
- quota模式(额度限流):严格对齐时间窗口,例如1m对齐到整分钟,1s对齐到整秒。
函数签名:
javascript
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"时超限会等待 |
支持的函数列表:
交易类:CreateOrder, CancelOrder
账户类:GetAccount, GetPositions
订单类:GetOrder, GetOrders, GetHistoryOrders
行情类:GetTicker, GetDepth, GetRecords
其他:IO/api
注意事项:
- 优宽量化支持的市场:商品期货(如螺纹钢rb888、豆粕m888等)
- 使用API限流功能前,必须先检查
exchange.IO("status")确保与交易服务器连接正常 - 建议配合
exchange.IO("mode", 0)立即返回模式使用,避免不必要的等待 IO/api的限流仅对exchange.IO("api", ...)调用生效- quota模式严格对齐时间窗口,例如设置
"1s"时,时间窗口为整秒对齐 - 使用
"delay"参数时,调用时间与日志记录时间可能存在差异,这是正常现象