futu_mcp/handlers/reference/
futures.rs1use std::sync::Arc;
5
6use anyhow::{Result, anyhow, bail};
7use futu_net::client::FutuClient;
8use prost::Message;
9use serde::Serialize;
10
11use crate::state::parse_symbol;
12
13#[derive(Serialize)]
18struct FutureInfoOut {
19 code: String,
20 name: String,
21 contract_type: String,
22 contract_size: f64,
23 last_trade_time: String,
24 trade_time_list: Vec<String>,
26}
27
28pub async fn get_future_info(client: &Arc<FutuClient>, symbols: &[String]) -> Result<String> {
29 if symbols.is_empty() {
32 bail!("future_info: symbols empty (必须至少传入 1 个 MARKET.CODE)");
33 }
34 let sec_list: Vec<_> = symbols
35 .iter()
36 .enumerate()
37 .map(|(i, s)| {
38 parse_symbol(s).map_err(|e| {
39 anyhow!("future_info: symbols[{i}] invalid ({s:?}): {e} — 整体 reject")
40 })
41 })
42 .collect::<Result<Vec<_>>>()?;
43 let proto_secs: Vec<_> = sec_list
44 .iter()
45 .map(|s| futu_proto::qot_common::Security {
46 market: s.market as i32,
47 code: s.code.clone(),
48 })
49 .collect();
50 let _parsed = futu_qot::symbol_list::parse_required_symbol_list(&proto_secs)
52 .map_err(|e| anyhow!("future_info: {e}"))?;
53 let req = futu_proto::qot_get_future_info::Request {
54 c2s: futu_proto::qot_get_future_info::C2s {
55 security_list: proto_secs,
56 header: None,
57 },
58 };
59 let body = req.encode_to_vec();
60 let frame = client
61 .request(futu_core::proto_id::QOT_GET_FUTURE_INFO, body)
62 .await?;
63 let resp = futu_proto::qot_get_future_info::Response::decode(frame.body.as_ref())
64 .map_err(|e| anyhow!("decode future_info: {e}"))?;
65 if resp.ret_type != 0 {
66 bail!(
67 "future_info ret_type={} msg={:?}",
68 resp.ret_type,
69 resp.ret_msg
70 );
71 }
72 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
73 let out: Vec<FutureInfoOut> = s2c
74 .future_info_list
75 .iter()
76 .map(|f| FutureInfoOut {
77 code: f.security.code.clone(),
78 name: f.name.clone(),
79 contract_type: f.contract_type.clone(),
80 contract_size: f.contract_size,
81 last_trade_time: f.last_trade_time.clone(),
82 trade_time_list: f
83 .trade_time
84 .iter()
85 .map(|t| {
86 format!(
87 "{}-{}",
88 t.begin.unwrap_or_default(),
89 t.end.unwrap_or_default()
90 )
91 })
92 .collect(),
93 })
94 .collect();
95 Ok(serde_json::to_string_pretty(&out)?)
96}