futu_mcp/handlers/reference/
price_reminder.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
13pub struct SetPriceReminderInput<'a> {
23 pub symbol: &'a str,
24 pub op: i32,
25 pub key: Option<i64>,
26 pub reminder_type: Option<i32>,
27 pub freq: Option<i32>,
28 pub value: Option<f64>,
29 pub note: Option<&'a str>,
30 pub reminder_session_list: &'a [i32],
31}
32
33pub async fn set_price_reminder(
34 client: &Arc<FutuClient>,
35 input: SetPriceReminderInput<'_>,
36) -> Result<String> {
37 let s = parse_symbol(input.symbol)?;
38 let req = futu_proto::qot_set_price_reminder::Request {
39 c2s: futu_proto::qot_set_price_reminder::C2s {
40 security: futu_proto::qot_common::Security {
41 market: s.market as i32,
42 code: s.code,
43 },
44 op: input.op,
45 key: input.key,
46 r#type: input.reminder_type,
47 freq: input.freq,
48 value: input.value,
49 note: input.note.map(String::from),
50 reminder_session_list: input.reminder_session_list.to_vec(),
51 header: None, },
53 };
54 let body = req.encode_to_vec();
55 let frame = client
56 .request(futu_core::proto_id::QOT_SET_PRICE_REMINDER, body)
57 .await?;
58 let resp = futu_proto::qot_set_price_reminder::Response::decode(frame.body.as_ref())
59 .map_err(|e| anyhow!("decode set_price_reminder: {e}"))?;
60 if resp.ret_type != 0 {
61 bail!(
62 "set_price_reminder ret_type={} msg={:?}",
63 resp.ret_type,
64 resp.ret_msg
65 );
66 }
67 let key_out = resp.s2c.map(|s| s.key);
68 Ok(serde_json::to_string_pretty(&serde_json::json!({
69 "ok": true,
70 "op": input.op,
71 "key": key_out,
72 }))?)
73}
74
75#[derive(Serialize)]
80struct PriceReminderItemOut {
81 key: i64,
82 reminder_type: i32,
83 value: f64,
84 note: String,
85 freq: i32,
86}
87
88#[derive(Serialize)]
89struct PriceReminderOut {
90 code: String,
91 name: String,
92 reminder_list: Vec<PriceReminderItemOut>,
93}
94
95pub async fn get_price_reminder(
98 client: &Arc<FutuClient>,
99 symbol: Option<&str>,
100 market: Option<i32>,
101) -> Result<String> {
102 let (security, market_code) = match symbol {
103 Some(s) => {
104 let sec = parse_symbol(s)?;
105 (
106 Some(futu_proto::qot_common::Security {
107 market: sec.market as i32,
108 code: sec.code,
109 }),
110 None,
111 )
112 }
113 None => (None, market),
114 };
115 if security.is_none() && market_code.is_none() {
116 bail!("price_reminder: either symbol or market required");
117 }
118 let req = futu_proto::qot_get_price_reminder::Request {
119 c2s: futu_proto::qot_get_price_reminder::C2s {
120 security,
121 market: market_code,
122 header: None,
123 },
124 };
125 let body = req.encode_to_vec();
126 let frame = client
127 .request(futu_core::proto_id::QOT_GET_PRICE_REMINDER, body)
128 .await?;
129 let resp = futu_proto::qot_get_price_reminder::Response::decode(frame.body.as_ref())
130 .map_err(|e| anyhow!("decode price_reminder: {e}"))?;
131 if resp.ret_type != 0 {
132 bail!(
133 "price_reminder ret_type={} msg={:?}",
134 resp.ret_type,
135 resp.ret_msg
136 );
137 }
138 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
139 let out: Vec<PriceReminderOut> = s2c
140 .price_reminder_list
141 .iter()
142 .map(|p| PriceReminderOut {
143 code: p.security.code.clone(),
144 name: p.name.clone().unwrap_or_default(),
145 reminder_list: p
146 .item_list
147 .iter()
148 .map(|r| PriceReminderItemOut {
149 key: r.key,
150 reminder_type: r.r#type,
151 value: r.value,
152 note: r.note.clone(),
153 freq: r.freq,
154 })
155 .collect(),
156 })
157 .collect();
158 Ok(serde_json::to_string_pretty(&out)?)
159}
160
161#[derive(Serialize)]
166struct OptionExpirationOut {
167 strike_time: Option<String>,
168 distance_days: i32,
169 cycle: Option<i32>,
170}
171
172pub async fn get_option_expiration_date(
174 client: &Arc<FutuClient>,
175 owner_symbol: &str,
176 index_option_type: Option<i32>,
177) -> Result<String> {
178 let s = parse_symbol(owner_symbol)?;
179 let req = futu_proto::qot_get_option_expiration_date::Request {
180 c2s: futu_proto::qot_get_option_expiration_date::C2s {
181 owner: futu_proto::qot_common::Security {
182 market: s.market as i32,
183 code: s.code,
184 },
185 index_option_type,
186 header: None, },
188 };
189 let body = req.encode_to_vec();
190 let frame = client
191 .request(futu_core::proto_id::QOT_GET_OPTION_EXPIRATION_DATE, body)
192 .await?;
193 let resp = futu_proto::qot_get_option_expiration_date::Response::decode(frame.body.as_ref())
194 .map_err(|e| anyhow!("decode option_expiration_date: {e}"))?;
195 if resp.ret_type != 0 {
196 bail!(
197 "option_expiration_date ret_type={} msg={:?}",
198 resp.ret_type,
199 resp.ret_msg
200 );
201 }
202 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
203 let out: Vec<OptionExpirationOut> = s2c
204 .date_list
205 .iter()
206 .map(|d| OptionExpirationOut {
207 strike_time: d.strike_time.clone(),
208 distance_days: d.option_expiry_date_distance,
209 cycle: d.cycle,
210 })
211 .collect();
212 Ok(serde_json::to_string_pretty(&out)?)
213}