Skip to main content

futucli/cmd/
orderbook.rs

1//! `futucli orderbook` — 获取摆盘数据
2
3use std::time::Duration;
4
5use anyhow::Result;
6use serde::Serialize;
7use tabled::Tabled;
8
9use crate::common::{connect_gateway, format_symbol, parse_symbol};
10use crate::output::OutputFormat;
11use crate::qot_sdk_adapter;
12
13#[derive(Tabled)]
14struct OrderBookRow {
15    #[tabled(rename = "Side")]
16    side: String,
17    #[tabled(rename = "Level")]
18    level: i32,
19    #[tabled(rename = "Price")]
20    price: String,
21    #[tabled(rename = "Volume")]
22    volume: String,
23    #[tabled(rename = "Orders")]
24    orders: i32,
25}
26
27#[derive(Serialize)]
28struct OrderBookJson {
29    symbol: String,
30    odd_lot: bool,
31    bids: Vec<Level>,
32    asks: Vec<Level>,
33}
34
35#[derive(Serialize)]
36struct Level {
37    price: f64,
38    volume: i64,
39    orders: i32,
40}
41
42pub async fn run(
43    gateway: &str,
44    symbol: &str,
45    depth: i32,
46    odd_lot: bool,
47    format: OutputFormat,
48) -> Result<()> {
49    let sec = parse_symbol(symbol)?;
50    let (client, _push_rx) = connect_gateway(gateway, "futucli-orderbook").await?;
51    let sub_type = qot_sdk_adapter::order_book_sub_type(odd_lot);
52    let order_book_type = odd_lot.then_some(1);
53
54    // 订阅 OrderBook,首次推送会携带完整快照
55    futu_qot::sub::subscribe(&client, std::slice::from_ref(&sec), &[sub_type], true, true).await?;
56    tokio::time::sleep(Duration::from_millis(300)).await;
57
58    let ob = futu_qot::order_book::get_order_book_with_type(&client, &sec, depth, order_book_type)
59        .await?;
60
61    // 表格:先卖档(高到低),再买档(高到低)
62    let mut rows = Vec::new();
63    for (i, a) in ob.ask_list.iter().enumerate().rev() {
64        rows.push(OrderBookRow {
65            side: "ASK".to_string(),
66            level: (i + 1) as i32,
67            price: format!("{:.3}", a.price),
68            volume: a.volume.to_string(),
69            orders: a.order_count,
70        });
71    }
72    for (i, b) in ob.bid_list.iter().enumerate() {
73        rows.push(OrderBookRow {
74            side: "BID".to_string(),
75            level: (i + 1) as i32,
76            price: format!("{:.3}", b.price),
77            volume: b.volume.to_string(),
78            orders: b.order_count,
79        });
80    }
81
82    let json = OrderBookJson {
83        symbol: format_symbol(&ob.security),
84        odd_lot,
85        bids: ob
86            .bid_list
87            .iter()
88            .map(|e| Level {
89                price: e.price,
90                volume: e.volume,
91                orders: e.order_count,
92            })
93            .collect(),
94        asks: ob
95            .ask_list
96            .iter()
97            .map(|e| Level {
98                price: e.price,
99                volume: e.volume,
100                orders: e.order_count,
101            })
102            .collect(),
103    };
104
105    format.print_rows(&rows, std::slice::from_ref(&json))?;
106    Ok(())
107}