1use anyhow::{Result, bail};
4use serde::Serialize;
5use tabled::Tabled;
6
7use crate::common::{connect_gateway, format_symbol, parse_symbol};
8use crate::output::OutputFormat;
9use futu_qot::types::QotMarket;
10
11fn parse_market(s: &str) -> Result<QotMarket> {
13 let m = match s.trim().to_ascii_uppercase().as_str() {
14 "HK" => QotMarket::HkSecurity,
15 "HK_FUTURE" => QotMarket::HkFuture,
16 "US" => QotMarket::UsSecurity,
17 "SH" => QotMarket::CnshSecurity,
18 "SZ" => QotMarket::CnszSecurity,
19 other => bail!("unknown market {other:?} (HK|HK_FUTURE|US|SH|SZ)"),
20 };
21 Ok(m)
22}
23
24fn parse_plate_set(s: &str) -> Result<i32> {
26 let v = match s.trim().to_ascii_lowercase().as_str() {
27 "all" => 0,
28 "industry" => 1,
29 "region" => 2,
30 "concept" => 3,
31 other => bail!("unknown plate-set {other:?} (all|industry|region|concept)"),
32 };
33 Ok(v)
34}
35
36#[derive(Tabled)]
37struct PlateRow {
38 #[tabled(rename = "Plate")]
39 plate: String,
40 #[tabled(rename = "Name")]
41 name: String,
42 #[tabled(rename = "Type")]
43 plate_type: String,
44}
45
46#[derive(Serialize)]
47struct PlateJson {
48 plate: String,
49 name: String,
50 plate_type: Option<i32>,
51}
52
53pub async fn list(gateway: &str, market: &str, set: &str, format: OutputFormat) -> Result<()> {
54 let m = parse_market(market)?;
55 let set_type = parse_plate_set(set)?;
56
57 let (client, _push_rx) = connect_gateway(gateway, "futucli-plate-list").await?;
58 let plates = futu_qot::market_misc::get_plate_set(&client, m, set_type).await?;
59
60 let rows: Vec<PlateRow> = plates
61 .iter()
62 .map(|p| PlateRow {
63 plate: format_symbol(&p.security),
64 name: p.name.clone(),
65 plate_type: p.plate_type.map(|v| v.to_string()).unwrap_or_default(),
66 })
67 .collect();
68
69 let jsons: Vec<PlateJson> = plates
70 .iter()
71 .map(|p| PlateJson {
72 plate: format_symbol(&p.security),
73 name: p.name.clone(),
74 plate_type: p.plate_type,
75 })
76 .collect();
77
78 format.print_rows(&rows, &jsons)?;
79 Ok(())
80}
81
82#[derive(Tabled)]
83struct StockRow {
84 #[tabled(rename = "Symbol")]
85 symbol: String,
86 #[tabled(rename = "Name")]
87 name: String,
88 #[tabled(rename = "ID")]
89 id: String,
90 #[tabled(rename = "Type")]
91 sec_type: i32,
92 #[tabled(rename = "Lot")]
93 lot_size: i32,
94}
95
96#[derive(Serialize)]
97struct StockJson {
98 symbol: String,
99 id: i64,
100 name: String,
101 sec_type: i32,
102 lot_size: i32,
103 list_time: String,
104 delisting: bool,
105}
106
107pub async fn stocks(gateway: &str, plate_symbol: &str, format: OutputFormat) -> Result<()> {
108 let plate = parse_symbol(plate_symbol)?;
109 let (client, _push_rx) = connect_gateway(gateway, "futucli-plate-stocks").await?;
110 let list = futu_qot::market_misc::get_plate_security(&client, &plate).await?;
111
112 let rows: Vec<StockRow> = list
113 .iter()
114 .map(|i| StockRow {
115 symbol: format_symbol(&i.security),
116 name: i.name.clone(),
117 id: i.id.to_string(),
118 sec_type: i.sec_type,
119 lot_size: i.lot_size,
120 })
121 .collect();
122
123 let jsons: Vec<StockJson> = list
124 .iter()
125 .map(|i| StockJson {
126 symbol: format_symbol(&i.security),
127 id: i.id,
128 name: i.name.clone(),
129 sec_type: i.sec_type,
130 lot_size: i.lot_size,
131 list_time: i.list_time.clone(),
132 delisting: i.delisting,
133 })
134 .collect();
135
136 format.print_rows(&rows, &jsons)?;
137 Ok(())
138}