1#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct Security {
7 pub market: QotMarket,
9 pub code: String,
11}
12
13impl Security {
14 pub fn new(market: QotMarket, code: impl Into<String>) -> Self {
16 Self {
17 market,
18 code: code.into(),
19 }
20 }
21
22 pub fn from_proto(s: &futu_proto::qot_common::Security) -> Self {
24 Self {
25 market: QotMarket::from_i32(s.market),
26 code: s.code.clone(),
27 }
28 }
29
30 pub fn to_proto(&self) -> futu_proto::qot_common::Security {
32 futu_proto::qot_common::Security {
33 market: self.market as i32,
34 code: self.code.clone(),
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41#[repr(i32)]
42#[non_exhaustive]
43pub enum QotMarket {
44 Unknown = 0,
45 HkSecurity = 1,
46 HkFuture = 2,
47 UsSecurity = 11,
48 CnshSecurity = 21,
49 CnszSecurity = 22,
50 SgSecurity = 31,
51 JpSecurity = 41,
52 AuSecurity = 51,
53 MySecurity = 61,
54 CaSecurity = 71,
55 FxSecurity = 81,
56 Crypto = 91,
60 EventContract = 101,
64}
65
66impl QotMarket {
67 pub fn from_i32(v: i32) -> Self {
69 match v {
70 1 => Self::HkSecurity,
71 2 => Self::HkFuture,
72 11 => Self::UsSecurity,
73 21 => Self::CnshSecurity,
74 22 => Self::CnszSecurity,
75 31 => Self::SgSecurity,
76 41 => Self::JpSecurity,
77 51 => Self::AuSecurity,
78 61 => Self::MySecurity,
79 71 => Self::CaSecurity,
80 81 => Self::FxSecurity,
81 91 => Self::Crypto,
82 101 => Self::EventContract,
83 _ => Self::Unknown,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90#[repr(i32)]
91#[non_exhaustive]
92pub enum SubType {
93 None = 0,
94 Basic = 1,
95 OrderBook = 2,
96 OrderBookOdd = 22,
97 Ticker = 4,
98 RT = 5,
99 KLDay = 6,
100 KL5Min = 7,
101 KL15Min = 8,
102 KL30Min = 9,
103 KL60Min = 10,
104 KL1Min = 11,
105 KLWeek = 12,
106 KLMonth = 13,
107 Broker = 14,
108 KLQuarter = 15,
109 KLYear = 16,
110 KL3Min = 17,
111 KL10Min = 18,
112 KL120Min = 19,
113 KL180Min = 20,
114 KL240Min = 21,
115 #[deprecated(
123 since = "1.4.113",
124 note = "OrderDetail is not a Qot_Common.SubType; use the order-detail API surface"
125 )]
126 OrderDetail = -3016,
127}
128
129impl SubType {
130 pub const VALID_PUBLIC: &'static [Self] = &[
135 Self::Basic,
136 Self::OrderBook,
137 Self::Ticker,
138 Self::RT,
139 Self::KLDay,
140 Self::KL5Min,
141 Self::KL15Min,
142 Self::KL30Min,
143 Self::KL60Min,
144 Self::KL1Min,
145 Self::KLWeek,
146 Self::KLMonth,
147 Self::Broker,
148 Self::KLQuarter,
149 Self::KLYear,
150 Self::KL3Min,
151 Self::KL10Min,
152 Self::KL120Min,
153 Self::KL180Min,
154 Self::KL240Min,
155 Self::OrderBookOdd,
156 ];
157
158 pub const VALID_PUBLIC_INT_VALUES: &'static [i32] = &[
159 Self::Basic as i32,
160 Self::OrderBook as i32,
161 Self::Ticker as i32,
162 Self::RT as i32,
163 Self::KLDay as i32,
164 Self::KL5Min as i32,
165 Self::KL15Min as i32,
166 Self::KL30Min as i32,
167 Self::KL60Min as i32,
168 Self::KL1Min as i32,
169 Self::KLWeek as i32,
170 Self::KLMonth as i32,
171 Self::Broker as i32,
172 Self::KLQuarter as i32,
173 Self::KLYear as i32,
174 Self::KL3Min as i32,
175 Self::KL10Min as i32,
176 Self::KL120Min as i32,
177 Self::KL180Min as i32,
178 Self::KL240Min as i32,
179 Self::OrderBookOdd as i32,
180 ];
181
182 pub const fn as_i32(self) -> i32 {
183 self as i32
184 }
185
186 pub const fn from_i32(v: i32) -> Option<Self> {
187 Some(match v {
188 0 => Self::None,
189 1 => Self::Basic,
190 2 => Self::OrderBook,
191 4 => Self::Ticker,
192 5 => Self::RT,
193 6 => Self::KLDay,
194 7 => Self::KL5Min,
195 8 => Self::KL15Min,
196 9 => Self::KL30Min,
197 10 => Self::KL60Min,
198 11 => Self::KL1Min,
199 12 => Self::KLWeek,
200 13 => Self::KLMonth,
201 14 => Self::Broker,
202 15 => Self::KLQuarter,
203 16 => Self::KLYear,
204 17 => Self::KL3Min,
205 18 => Self::KL10Min,
206 19 => Self::KL120Min,
207 20 => Self::KL180Min,
208 21 => Self::KL240Min,
209 22 => Self::OrderBookOdd,
210 _ => return None,
211 })
212 }
213
214 pub const fn from_public_i32(v: i32) -> Option<Self> {
215 match Self::from_i32(v) {
216 Some(Self::None) | None => None,
217 Some(sub_type) => Some(sub_type),
218 }
219 }
220
221 pub fn from_str_alias(s: &str) -> Option<Self> {
222 futu_core::qot_subscription::qot_sub_type_from_str_alias(s).and_then(Self::from_i32)
223 }
224
225 pub const fn is_kline(self) -> bool {
226 matches!(
227 self,
228 Self::KLDay
229 | Self::KL5Min
230 | Self::KL15Min
231 | Self::KL30Min
232 | Self::KL60Min
233 | Self::KL1Min
234 | Self::KLWeek
235 | Self::KLMonth
236 | Self::KLQuarter
237 | Self::KLYear
238 | Self::KL3Min
239 | Self::KL10Min
240 | Self::KL120Min
241 | Self::KL180Min
242 | Self::KL240Min
243 )
244 }
245
246 pub const fn kl_type(self) -> Option<KLType> {
247 Some(match self {
248 Self::KL1Min => KLType::Min1,
249 Self::KLDay => KLType::Day,
250 Self::KLWeek => KLType::Week,
251 Self::KLMonth => KLType::Month,
252 Self::KLYear => KLType::Year,
253 Self::KL5Min => KLType::Min5,
254 Self::KL15Min => KLType::Min15,
255 Self::KL30Min => KLType::Min30,
256 Self::KL60Min => KLType::Min60,
257 Self::KL3Min => KLType::Min3,
258 Self::KLQuarter => KLType::Quarter,
259 Self::KL10Min => KLType::Min10,
260 Self::KL120Min => KLType::Min120,
261 Self::KL180Min => KLType::Min180,
262 Self::KL240Min => KLType::Min240,
263 _ => return None,
264 })
265 }
266
267 pub const fn short_label(self) -> Option<&'static str> {
268 futu_core::qot_subscription::qot_sub_type_short_label(self as i32)
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
274#[repr(i32)]
275#[non_exhaustive]
276pub enum KLType {
277 Unknown = 0,
278 Min1 = 1,
279 Day = 2,
280 Week = 3,
281 Month = 4,
282 Year = 5,
283 Min5 = 6,
284 Min15 = 7,
285 Min30 = 8,
286 Min60 = 9,
287 Min3 = 10,
288 Quarter = 11,
289 Min10 = 12,
290 Min120 = 13,
291 Min180 = 14,
292 Min240 = 15,
293}
294
295impl KLType {
296 pub const VALID_PUBLIC: &'static [Self] = &[
297 Self::Min1,
298 Self::Day,
299 Self::Week,
300 Self::Month,
301 Self::Year,
302 Self::Min5,
303 Self::Min15,
304 Self::Min30,
305 Self::Min60,
306 Self::Min3,
307 Self::Quarter,
308 Self::Min10,
309 Self::Min120,
310 Self::Min180,
311 Self::Min240,
312 ];
313
314 pub const fn as_i32(self) -> i32 {
315 self as i32
316 }
317
318 pub const fn from_i32(v: i32) -> Option<Self> {
319 Some(match v {
320 0 => Self::Unknown,
321 1 => Self::Min1,
322 2 => Self::Day,
323 3 => Self::Week,
324 4 => Self::Month,
325 5 => Self::Year,
326 6 => Self::Min5,
327 7 => Self::Min15,
328 8 => Self::Min30,
329 9 => Self::Min60,
330 10 => Self::Min3,
331 11 => Self::Quarter,
332 12 => Self::Min10,
333 13 => Self::Min120,
334 14 => Self::Min180,
335 15 => Self::Min240,
336 _ => return None,
337 })
338 }
339
340 pub const fn from_public_i32(v: i32) -> Option<Self> {
341 match Self::from_i32(v) {
342 Some(Self::Unknown) | None => None,
343 Some(kl_type) => Some(kl_type),
344 }
345 }
346
347 pub fn from_str_alias(s: &str) -> Option<Self> {
348 futu_core::qot_subscription::qot_kl_type_from_str_alias(s).and_then(Self::from_i32)
349 }
350
351 pub const fn is_minute(self) -> bool {
352 futu_core::qot_subscription::is_minute_kl_type(self as i32)
353 }
354
355 pub const fn sub_type(self) -> Option<SubType> {
356 match futu_core::qot_subscription::sub_type_for_kl_type(self as i32) {
357 Some(sub_type) => SubType::from_i32(sub_type),
358 None => None,
359 }
360 }
361
362 pub const fn backend_kline_type(self) -> Option<u32> {
367 futu_core::qot_subscription::qot_kl_type_backend_kline_type(self as i32)
368 }
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373#[repr(i32)]
374#[non_exhaustive]
375pub enum RehabType {
376 None = 0,
377 Forward = 1,
378 Backward = 2,
379}
380
381#[derive(Debug, Clone)]
383pub struct KLine {
384 pub time: String,
386 pub is_blank: bool,
388 pub high_price: f64,
390 pub open_price: f64,
392 pub low_price: f64,
394 pub close_price: f64,
396 pub last_close_price: f64,
398 pub volume: i64,
400 pub turnover: f64,
402 pub turnover_rate: f64,
404 pub pe: f64,
406 pub change_rate: f64,
408 pub timestamp: f64,
410}
411
412impl KLine {
413 pub fn from_proto(k: &futu_proto::qot_common::KLine) -> Self {
414 Self {
415 time: k.time.clone(),
416 is_blank: k.is_blank,
417 high_price: k.high_price.unwrap_or(0.0),
418 open_price: k.open_price.unwrap_or(0.0),
419 low_price: k.low_price.unwrap_or(0.0),
420 close_price: k.close_price.unwrap_or(0.0),
421 last_close_price: k.last_close_price.unwrap_or(0.0),
422 volume: k.volume.unwrap_or(0),
423 turnover: k.turnover.unwrap_or(0.0),
424 turnover_rate: k.turnover_rate.unwrap_or(0.0),
425 pe: k.pe.unwrap_or(0.0),
426 change_rate: k.change_rate.unwrap_or(0.0),
427 timestamp: k.timestamp.unwrap_or(0.0),
428 }
429 }
430}
431
432#[derive(Debug, Clone)]
434pub struct BasicQot {
435 pub security: Security,
437 pub is_suspended: bool,
439 pub list_time: String,
441 pub price_spread: f64,
443 pub update_time: String,
445 pub high_price: f64,
447 pub open_price: f64,
449 pub low_price: f64,
451 pub cur_price: f64,
453 pub last_close_price: f64,
455 pub volume: i64,
457 pub turnover: f64,
459 pub turnover_rate: f64,
461 pub amplitude: f64,
463}
464
465impl BasicQot {
466 pub fn from_proto(q: &futu_proto::qot_common::BasicQot) -> Self {
467 Self {
468 security: Security::from_proto(&q.security),
469 is_suspended: q.is_suspended,
470 list_time: q.list_time.clone(),
471 price_spread: q.price_spread,
472 update_time: q.update_time.clone(),
473 high_price: q.high_price,
474 open_price: q.open_price,
475 low_price: q.low_price,
476 cur_price: q.cur_price,
477 last_close_price: q.last_close_price,
478 volume: q.volume,
479 turnover: q.turnover,
480 turnover_rate: q.turnover_rate,
481 amplitude: q.amplitude,
482 }
483 }
484}
485
486#[derive(Debug, Clone)]
488pub struct OrderBookEntry {
489 pub price: f64,
491 pub volume: i64,
493 pub order_count: i32,
495}
496
497impl OrderBookEntry {
498 pub fn from_proto(ob: &futu_proto::qot_common::OrderBook) -> Self {
499 Self {
500 price: ob.price,
501 volume: ob.volume,
502 order_count: ob.oreder_count, }
504 }
505}
506
507#[derive(Debug, Clone)]
509pub struct OrderBookData {
510 pub security: Security,
512 pub ask_list: Vec<OrderBookEntry>,
514 pub bid_list: Vec<OrderBookEntry>,
516}
517
518pub fn market_state_label(s: i32) -> &'static str {
527 futu_core::market::qot_market_state_label(s)
528}
529
530#[cfg(test)]
531mod tests;