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 futu_qot::types::SubType;
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 = if odd_lot {
52        SubType::OrderBookOdd
53    } else {
54        SubType::OrderBook
55    };
56    let order_book_type = odd_lot.then_some(1);
57
58    // 订阅 OrderBook,首次推送会携带完整快照
59    futu_qot::sub::subscribe(&client, std::slice::from_ref(&sec), &[sub_type], true, true).await?;
60    tokio::time::sleep(Duration::from_millis(300)).await;
61
62    let ob = futu_qot::order_book::get_order_book_with_type(&client, &sec, depth, order_book_type)
63        .await?;
64
65    // 表格:先卖档(高到低),再买档(高到低)
66    let mut rows = Vec::new();
67    for (i, a) in ob.ask_list.iter().enumerate().rev() {
68        rows.push(OrderBookRow {
69            side: "ASK".to_string(),
70            level: (i + 1) as i32,
71            price: format!("{:.3}", a.price),
72            volume: a.volume.to_string(),
73            orders: a.order_count,
74        });
75    }
76    for (i, b) in ob.bid_list.iter().enumerate() {
77        rows.push(OrderBookRow {
78            side: "BID".to_string(),
79            level: (i + 1) as i32,
80            price: format!("{:.3}", b.price),
81            volume: b.volume.to_string(),
82            orders: b.order_count,
83        });
84    }
85
86    let json = OrderBookJson {
87        symbol: format_symbol(&ob.security),
88        odd_lot,
89        bids: ob
90            .bid_list
91            .iter()
92            .map(|e| Level {
93                price: e.price,
94                volume: e.volume,
95                orders: e.order_count,
96            })
97            .collect(),
98        asks: ob
99            .ask_list
100            .iter()
101            .map(|e| Level {
102                price: e.price,
103                volume: e.volume,
104                orders: e.order_count,
105            })
106            .collect(),
107    };
108
109    format.print_rows(&rows, std::slice::from_ref(&json))?;
110    Ok(())
111}