事件驱动
除了按固定间隔轮询,策略也可以等待事件到达后再处理,减少无效请求,也能更快地响应行情变化。
EventLoop
EventLoop 等待exchange.Go()、HttpQuery_Go()等并发任务完成、WebSocket 连接有可读数据、线程消息等事件;有事件时返回事件信息,策略再去读取对应的数据。第一次调用EventLoop()时才开始记录事件,所以先调用一次EventLoop(-1)再发起并发任务:
javascript
function main() {
EventLoop(-1) // 开始记录事件,避免错过之后发生的事件
var r1 = exchange.Go("GetTicker")
var r2 = exchange.Go("GetDepth")
var ev = EventLoop(1000) // 等待任意一个并发任务完成,最多等 1 秒
Log("event:", ev)
Log("ticker:", r1.wait(), "depth:", r2.wait())
}
ctx.subscribe / ctx.poll
JavaScript和Rust策略还可以使用托管者的事件订阅接口:ctx.subscribe()订阅某个账户、某个品种的行情或订单回报,返回流 ID;ctx.poll()取出下一条事件(可以设置等待超时),策略按事件的kind分别处理。Python策略不支持。
javascript
function main() {
ctx.subscribe(0, "BTC_USDT", {channel: "ticker"}) // 第一个参数是账户在 exchanges 中的下标
ctx.subscribe(0, "", {channel: "orders"}) // 订单回报
while (true) {
const ev = ctx.poll([], 1000) // [] 表示所有订阅,最多等 1 秒
if (!ev) {
continue
}
if (ev.kind === 1) {
Log("ticker:", ev.symbol, ev.bid, ev.ask, ev.last)
} else if (ev.kind === 16) {
Log("order:", ev.id, ev.state, ev.filledQty)
}
}
}
channel可选"ticker"、"bbo"、"depth"、"trade"、"kline"(interval为周期秒数)、"orders"。- 事件的
kind:1 为 ticker,3 为深度(事件只表示订单簿已更新,档位用ctx.book(ev.ex, ev.symbol, n)读取),4 为成交,5 为 K 线,16 为订单回报。 - 行情类订阅消费不及时会只保留最新数据或丢弃最旧的数据,订单回报不会丢弃,策略需要持续调用
ctx.poll()。
Rust策略中的写法为ctx::subscribe()、ctx::poll(),事件为原始结构,价格、数量是定点整数:
rust
fn main() {
let s = ctx::subscribe(0, "BTC_USDT", ctx::SubOpts::ticker()).unwrap();
loop {
match ctx::poll(&[s], Some(1000)) {
ctx::Polled::Event(ev) => Log!("event kind:", ev.kind),
ctx::Polled::Stopped => break,
_ => {}
}
}
}