1use chrono::{DateTime, Utc};
15use serde::{Deserialize, Deserializer, Serialize};
16use std::collections::HashMap;
17
18fn default_nan() -> f64 {
22 f64::NAN
23}
24
25fn deserialize_f64_or_nan<'de, D>(deserializer: D) -> Result<f64, D::Error>
27where
28 D: Deserializer<'de>,
29{
30 let opt = Option::<f64>::deserialize(deserializer)?;
31 Ok(opt.unwrap_or(f64::NAN))
32}
33
34fn deserialize_i64_or_zero<'de, D>(deserializer: D) -> Result<i64, D::Error>
36where
37 D: Deserializer<'de>,
38{
39 let opt = Option::<i64>::deserialize(deserializer)?;
40 Ok(opt.unwrap_or(0))
41}
42
43fn deserialize_f64_or_nan_or_dash<'de, D>(deserializer: D) -> Result<f64, D::Error>
45where
46 D: Deserializer<'de>,
47{
48 use serde::de::Error;
49 let value = serde_json::Value::deserialize(deserializer)?;
50 match value {
51 serde_json::Value::Number(n) => n.as_f64().ok_or_else(|| Error::custom("invalid number")),
52 serde_json::Value::String(s) if s == "-" => Ok(f64::NAN),
53 serde_json::Value::Null => Ok(f64::NAN),
54 _ => Err(Error::custom("expected number, string \"-\", or null")),
55 }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Quote {
63 pub instrument_id: String,
65 pub datetime: String,
67 #[serde(default = "default_nan")]
69 pub last_price: f64,
70
71 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
74 pub ask_price1: f64,
75 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
77 pub ask_volume1: i64,
78 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
80 pub ask_price2: f64,
81 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
83 pub ask_volume2: i64,
84 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
86 pub ask_price3: f64,
87 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
89 pub ask_volume3: i64,
90 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
92 pub ask_price4: f64,
93 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
95 pub ask_volume4: i64,
96 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
98 pub ask_price5: f64,
99 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
101 pub ask_volume5: i64,
102
103 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
105 pub bid_price1: f64,
106 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
108 pub bid_volume1: i64,
109 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
111 pub bid_price2: f64,
112 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
114 pub bid_volume2: i64,
115 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
117 pub bid_price3: f64,
118 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
120 pub bid_volume3: i64,
121 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
123 pub bid_price4: f64,
124 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
126 pub bid_volume4: i64,
127 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
129 pub bid_price5: f64,
130 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
132 pub bid_volume5: i64,
133
134 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
137 pub highest: f64,
138 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
140 pub lowest: f64,
141 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
143 pub open: f64,
144 #[serde(
146 default = "default_nan",
147 deserialize_with = "deserialize_f64_or_nan_or_dash"
148 )]
149 pub close: f64,
150 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
152 pub average: f64,
153 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
155 pub volume: i64,
156 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
158 pub amount: f64,
159 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
161 pub open_interest: i64,
162
163 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
166 pub lower_limit: f64,
167 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
169 pub upper_limit: f64,
170
171 #[serde(
174 default = "default_nan",
175 deserialize_with = "deserialize_f64_or_nan_or_dash"
176 )]
177 pub settlement: f64,
178 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
180 pub pre_settlement: f64,
181
182 #[serde(default = "default_nan")]
185 pub change: f64,
186 #[serde(default = "default_nan")]
188 pub change_percent: f64,
189
190 #[serde(default = "default_nan")]
193 pub strike_price: f64,
194
195 #[serde(default, deserialize_with = "deserialize_i64_or_zero")]
198 pub pre_open_interest: i64,
199 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
201 pub pre_close: f64,
202 #[serde(default)]
204 pub pre_volume: i64,
205
206 #[serde(default = "default_nan")]
209 pub margin: f64,
210 #[serde(default = "default_nan")]
212 pub commission: f64,
213
214 #[serde(default)]
217 pub class: String,
218 #[serde(default)]
220 pub exchange_id: String,
221 #[serde(default)]
223 pub product_id: String,
224 #[serde(default)]
226 pub product_short_name: String,
227 #[serde(default)]
229 pub underlying_product: String,
230 #[serde(default)]
232 pub underlying_symbol: String,
233 #[serde(default)]
235 pub delivery_year: i32,
236 #[serde(default)]
238 pub delivery_month: i32,
239 #[serde(default)]
241 pub expire_datetime: i64,
242 #[serde(default)]
244 pub volume_multiple: i32,
245 #[serde(default = "default_nan")]
247 pub price_tick: f64,
248 #[serde(default)]
250 pub price_decs: i32,
251 #[serde(default)]
253 pub max_market_order_vol: i32,
254 #[serde(default)]
256 pub min_market_order_vol: i32,
257 #[serde(default)]
259 pub max_limit_order_vol: i32,
260 #[serde(default)]
262 pub min_limit_order_vol: i32,
263 #[serde(default)]
265 pub expired: bool,
266 #[serde(default)]
268 pub py: String,
269
270 #[serde(skip_serializing_if = "Option::is_none")]
272 #[serde(rename = "_epoch")]
273 pub epoch: Option<i64>,
274}
275
276impl Default for Quote {
277 fn default() -> Self {
278 Quote {
279 instrument_id: String::new(),
280 datetime: String::new(),
281 last_price: f64::NAN,
282 ask_price1: f64::NAN,
283 ask_volume1: 0,
284 ask_price2: f64::NAN,
285 ask_volume2: 0,
286 ask_price3: f64::NAN,
287 ask_volume3: 0,
288 ask_price4: f64::NAN,
289 ask_volume4: 0,
290 ask_price5: f64::NAN,
291 ask_volume5: 0,
292 bid_price1: f64::NAN,
293 bid_volume1: 0,
294 bid_price2: f64::NAN,
295 bid_volume2: 0,
296 bid_price3: f64::NAN,
297 bid_volume3: 0,
298 bid_price4: f64::NAN,
299 bid_volume4: 0,
300 bid_price5: f64::NAN,
301 bid_volume5: 0,
302 highest: f64::NAN,
303 lowest: f64::NAN,
304 open: f64::NAN,
305 close: f64::NAN,
306 average: f64::NAN,
307 volume: 0,
308 amount: f64::NAN,
309 open_interest: 0,
310 lower_limit: f64::NAN,
311 upper_limit: f64::NAN,
312 settlement: f64::NAN,
313 pre_settlement: f64::NAN,
314 change: f64::NAN,
315 change_percent: f64::NAN,
316 strike_price: f64::NAN,
317 pre_open_interest: 0,
318 pre_close: f64::NAN,
319 pre_volume: 0,
320 margin: f64::NAN,
321 commission: f64::NAN,
322 class: String::new(),
323 exchange_id: String::new(),
324 product_id: String::new(),
325 product_short_name: String::new(),
326 underlying_product: String::new(),
327 underlying_symbol: String::new(),
328 delivery_year: 0,
329 delivery_month: 0,
330 expire_datetime: 0,
331 volume_multiple: 0,
332 price_tick: f64::NAN,
333 price_decs: 0,
334 max_market_order_vol: 0,
335 min_market_order_vol: 0,
336 max_limit_order_vol: 0,
337 min_limit_order_vol: 0,
338 expired: false,
339 py: String::new(),
340 epoch: None,
341 }
342 }
343}
344
345impl Quote {
346 pub fn update_change(&mut self) {
348 if !self.last_price.is_nan() && !self.pre_settlement.is_nan() && self.pre_settlement != 0.0
349 {
350 self.change = self.last_price - self.pre_settlement;
351 self.change_percent = self.change / self.pre_settlement * 100.0;
352 }
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 #[test]
361 fn test_quote_deserialize_with_nulls() {
362 let json_data = r#"{
363 "instrument_id":"DCE.m2512",
364 "datetime":"2025-11-24 22:59:59.000001",
365 "ask_price1":3005.0,
366 "ask_volume1":1,
367 "ask_price2":null,
368 "ask_volume2":null,
369 "bid_price1":2995.0,
370 "bid_volume1":2,
371 "bid_price2":null,
372 "bid_volume2":null,
373 "last_price":3000.0,
374 "highest":3000.0,
375 "lowest":2986.0,
376 "open":2998.0,
377 "close":"-",
378 "average":2995.0,
379 "volume":688,
380 "amount":20609740.0,
381 "open_interest":5278,
382 "settlement":"-",
383 "upper_limit":3181.0,
384 "lower_limit":2821.0,
385 "pre_open_interest":5729,
386 "pre_settlement":3001.0,
387 "pre_close":3001.0
388 }"#;
389
390 let result = serde_json::from_str::<Quote>(json_data);
391 assert!(result.is_ok(), "Quote 解析失败: {:?}", result.err());
392
393 let quote = result.unwrap();
394 assert_eq!(quote.instrument_id, "DCE.m2512");
395 assert_eq!(quote.last_price, 3000.0);
396 assert_eq!(quote.ask_price1, 3005.0);
397 assert!(quote.ask_price2.is_nan(), "null 应该被转换为 NaN");
398 assert_eq!(quote.ask_volume2, 0, "null 应该被转换为 0");
399 assert!(quote.close.is_nan(), "dash 应该被转换为 NaN");
400 assert!(quote.settlement.is_nan(), "dash 应该被转换为 NaN");
401 }
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct Kline {
409 #[serde(default)]
411 pub id: i64,
412 pub datetime: i64,
414 pub open: f64,
416 pub close: f64,
418 pub high: f64,
420 pub low: f64,
422 pub open_oi: i64,
424 pub close_oi: i64,
426 pub volume: i64,
428
429 #[serde(skip_serializing_if = "Option::is_none")]
431 #[serde(rename = "_epoch")]
432 pub epoch: Option<i64>,
433}
434
435impl Default for Kline {
436 fn default() -> Self {
437 Kline {
438 id: 0,
439 datetime: 0,
440 open: f64::NAN,
441 close: f64::NAN,
442 high: f64::NAN,
443 low: f64::NAN,
444 open_oi: 0,
445 close_oi: 0,
446 volume: 0,
447 epoch: None,
448 }
449 }
450}
451
452#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct Tick {
457 #[serde(default)]
459 pub id: i64,
460 pub datetime: i64,
462 pub last_price: f64,
464 pub average: f64,
466 pub highest: f64,
468 pub lowest: f64,
470
471 pub ask_price1: f64,
473 pub ask_volume1: i64,
474
475 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
476 pub ask_price2: f64,
477
478 pub ask_volume2: i64,
479
480 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
481 pub ask_price3: f64,
482 pub ask_volume3: i64,
483
484 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
485 pub ask_price4: f64,
486 pub ask_volume4: i64,
487
488 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
489 pub ask_price5: f64,
490 pub ask_volume5: i64,
491
492 pub bid_price1: f64,
493 pub bid_volume1: i64,
494
495 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
496 pub bid_price2: f64,
497 pub bid_volume2: i64,
498
499 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
500 pub bid_price3: f64,
501 pub bid_volume3: i64,
502
503 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
504 pub bid_price4: f64,
505 pub bid_volume4: i64,
506
507 #[serde(default = "default_nan", deserialize_with = "deserialize_f64_or_nan")]
508 pub bid_price5: f64,
509 pub bid_volume5: i64,
510
511 pub volume: i64,
513 pub amount: f64,
515 pub open_interest: i64,
517
518 #[serde(skip_serializing_if = "Option::is_none")]
520 #[serde(rename = "_epoch")]
521 pub epoch: Option<i64>,
522}
523
524impl Default for Tick {
525 fn default() -> Self {
526 Tick {
527 id: 0,
528 datetime: 0,
529 last_price: f64::NAN,
530 average: f64::NAN,
531 highest: f64::NAN,
532 lowest: f64::NAN,
533 ask_price1: f64::NAN,
534 ask_volume1: 0,
535 ask_price2: f64::NAN,
536 ask_volume2: 0,
537 ask_price3: f64::NAN,
538 ask_volume3: 0,
539 ask_price4: f64::NAN,
540 ask_volume4: 0,
541 ask_price5: f64::NAN,
542 ask_volume5: 0,
543 bid_price1: f64::NAN,
544 bid_volume1: 0,
545 bid_price2: f64::NAN,
546 bid_volume2: 0,
547 bid_price3: f64::NAN,
548 bid_volume3: 0,
549 bid_price4: f64::NAN,
550 bid_volume4: 0,
551 bid_price5: f64::NAN,
552 bid_volume5: 0,
553 volume: 0,
554 amount: f64::NAN,
555 open_interest: 0,
556 epoch: None,
557 }
558 }
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
565pub struct Chart {
566 pub left_id: i64,
568 pub right_id: i64,
570 pub more_data: bool,
572 pub ready: bool,
574 #[serde(default)]
576 pub state: HashMap<String, serde_json::Value>,
577
578 #[serde(skip_serializing_if = "Option::is_none")]
580 #[serde(rename = "_epoch")]
581 pub epoch: Option<i64>,
582}
583
584impl Default for Chart {
585 fn default() -> Self {
586 Chart {
587 left_id: -1,
588 right_id: -1,
589 more_data: true,
590 ready: false,
591 state: HashMap::new(),
592 epoch: None,
593 }
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct ChartInfo {
600 #[serde(default)]
601 pub chart_id: String,
602 pub left_id: i64,
603 pub right_id: i64,
604 pub more_data: bool,
605 pub ready: bool,
606 #[serde(default)]
607 pub view_width: usize,
608}
609
610#[derive(Debug, Clone)]
614pub struct KlineSeriesData {
615 pub symbol: String,
617 pub duration: i64,
619 pub chart_id: String,
621 pub chart: Option<ChartInfo>,
623 pub last_id: i64,
625 pub trading_day_start_id: i64,
627 pub trading_day_end_id: i64,
629 pub data: Vec<Kline>,
631 pub has_new_bar: bool,
633}
634
635#[derive(Debug, Clone)]
637pub struct TickSeriesData {
638 pub symbol: String,
640 pub chart_id: String,
642 pub chart: Option<ChartInfo>,
644 pub last_id: i64,
646 pub data: Vec<Tick>,
648 pub has_new_bar: bool,
650}
651
652#[derive(Debug, Clone)]
656pub struct AlignedKlineSet {
657 pub main_id: i64,
659 pub timestamp: DateTime<Utc>,
661 pub klines: HashMap<String, Kline>,
663}
664
665#[derive(Debug, Clone)]
667pub struct KlineMetadata {
668 pub symbol: String,
669 pub last_id: i64,
670 pub trading_day_start_id: i64,
671 pub trading_day_end_id: i64,
672}
673
674#[derive(Debug, Clone)]
676pub struct MultiKlineSeriesData {
677 pub chart_id: String,
679 pub duration: i64,
681 pub main_symbol: String,
683 pub symbols: Vec<String>,
685 pub left_id: i64,
687 pub right_id: i64,
689 pub view_width: usize,
691 pub data: Vec<AlignedKlineSet>,
693 pub has_new_bar: bool,
695 pub metadata: HashMap<String, KlineMetadata>,
697}
698
699#[derive(Debug, Clone)]
703pub struct SeriesData {
704 pub is_multi: bool,
706 pub is_tick: bool,
708 pub symbols: Vec<String>,
710 pub single: Option<KlineSeriesData>,
712 pub multi: Option<MultiKlineSeriesData>,
714 pub tick_data: Option<TickSeriesData>,
716}
717
718impl SeriesData {
719 pub fn get_symbol_klines(&self, symbol: &str) -> Option<&KlineSeriesData> {
721 if self.is_multi || self.symbols.is_empty() || self.symbols[0] != symbol {
722 return None;
723 }
724 self.single.as_ref()
725 }
726}
727
728#[derive(Debug, Clone)]
730pub struct UpdateInfo {
731 pub has_new_bar: bool,
733 pub new_bar_ids: HashMap<String, i64>,
735 pub has_bar_update: bool,
737 pub chart_range_changed: bool,
739 pub old_left_id: i64,
741 pub old_right_id: i64,
743 pub new_left_id: i64,
745 pub new_right_id: i64,
747 pub has_chart_sync: bool,
749 pub chart_ready: bool,
751}
752
753impl Default for UpdateInfo {
754 fn default() -> Self {
755 UpdateInfo {
756 has_new_bar: false,
757 new_bar_ids: HashMap::new(),
758 has_bar_update: false,
759 chart_range_changed: false,
760 old_left_id: -1,
761 old_right_id: -1,
762 new_left_id: -1,
763 new_right_id: -1,
764 has_chart_sync: false,
765 chart_ready: false,
766 }
767 }
768}
769
770#[derive(Debug, Clone, Serialize, Deserialize)]
774pub struct Account {
775 #[serde(default)]
777 pub user_id: String,
778 #[serde(default = "default_currency")]
780 pub currency: String,
781 #[serde(default)]
783 pub available: f64,
784 #[serde(default)]
786 pub balance: f64,
787 #[serde(default)]
789 pub close_profit: f64,
790 #[serde(default)]
792 pub commission: f64,
793 #[serde(default)]
795 pub ctp_available: f64,
796 #[serde(default)]
798 pub ctp_balance: f64,
799 #[serde(default)]
801 pub deposit: f64,
802 #[serde(default)]
804 pub float_profit: f64,
805 #[serde(default)]
807 pub frozen_commission: f64,
808 #[serde(default)]
810 pub frozen_margin: f64,
811 #[serde(default)]
813 pub frozen_premium: f64,
814 #[serde(default)]
816 pub margin: f64,
817 #[serde(default)]
819 pub market_value: f64,
820 #[serde(default)]
822 pub position_profit: f64,
823 #[serde(default)]
825 pub pre_balance: f64,
826 #[serde(default)]
828 pub premium: f64,
829 #[serde(default)]
831 pub risk_ratio: f64,
832 #[serde(default)]
834 pub static_balance: f64,
835 #[serde(default)]
837 pub withdraw: f64,
838
839 #[serde(skip_serializing_if = "Option::is_none")]
840 #[serde(rename = "_epoch")]
841 pub epoch: Option<i64>,
842}
843
844fn default_currency() -> String {
845 "CNY".to_string()
846}
847
848impl Account {
849 pub fn curr_margin(&self) -> f64 {
851 self.margin
852 }
853
854 pub fn _epoch(&self) -> Option<i64> {
856 self.epoch
857 }
858}
859
860#[derive(Debug, Clone, Serialize, Deserialize)]
862pub struct Position {
863 #[serde(default)]
865 pub user_id: String,
866 #[serde(default)]
868 pub exchange_id: String,
869 #[serde(default)]
871 pub instrument_id: String,
872 #[serde(default)]
874 pub volume_long_today: i64,
875 #[serde(default)]
877 pub volume_long_his: i64,
878 #[serde(default)]
880 pub volume_long: i64,
881 #[serde(default)]
883 pub volume_long_frozen_today: i64,
884 #[serde(default)]
886 pub volume_long_frozen_his: i64,
887 #[serde(default)]
889 pub volume_long_frozen: i64,
890 #[serde(default)]
892 pub volume_short_today: i64,
893 #[serde(default)]
895 pub volume_short_his: i64,
896 #[serde(default)]
898 pub volume_short: i64,
899 #[serde(default)]
901 pub volume_short_frozen_today: i64,
902 #[serde(default)]
904 pub volume_short_frozen_his: i64,
905 #[serde(default)]
907 pub volume_short_frozen: i64,
908 #[serde(default)]
910 pub volume_long_yd: i64,
911 #[serde(default)]
913 pub volume_short_yd: i64,
914 #[serde(default)]
916 pub pos_long_his: i64,
917 #[serde(default)]
919 pub pos_long_today: i64,
920 #[serde(default)]
922 pub pos_short_his: i64,
923 #[serde(default)]
925 pub pos_short_today: i64,
926 #[serde(default)]
928 pub open_price_long: f64,
929 #[serde(default)]
931 pub open_price_short: f64,
932 #[serde(default)]
934 pub open_cost_long: f64,
935 #[serde(default)]
937 pub open_cost_short: f64,
938 #[serde(default)]
940 pub position_price_long: f64,
941 #[serde(default)]
943 pub position_price_short: f64,
944 #[serde(default)]
946 pub position_cost_long: f64,
947 #[serde(default)]
949 pub position_cost_short: f64,
950 #[serde(default)]
952 pub last_price: f64,
953 #[serde(default)]
955 pub float_profit_long: f64,
956 #[serde(default)]
958 pub float_profit_short: f64,
959 #[serde(default)]
961 pub float_profit: f64,
962 #[serde(default)]
964 pub position_profit_long: f64,
965 #[serde(default)]
967 pub position_profit_short: f64,
968 #[serde(default)]
970 pub position_profit: f64,
971 #[serde(default)]
973 pub margin_long: f64,
974 #[serde(default)]
976 pub margin_short: f64,
977 #[serde(default)]
979 pub margin: f64,
980 #[serde(default)]
982 pub market_value_long: f64,
983 #[serde(default)]
985 pub market_value_short: f64,
986 #[serde(default)]
988 pub market_value: f64,
989
990 #[serde(skip_serializing_if = "Option::is_none")]
991 #[serde(rename = "_epoch")]
992 pub epoch: Option<i64>,
993}
994
995#[derive(Debug, Clone, Serialize, Deserialize)]
997pub struct Order {
998 #[serde(default)]
1000 pub seqno: i64,
1001 #[serde(default)]
1003 pub user_id: String,
1004 #[serde(default)]
1006 pub order_id: String,
1007 #[serde(default)]
1009 pub exchange_id: String,
1010 #[serde(default)]
1012 pub instrument_id: String,
1013 #[serde(default)]
1015 pub direction: String,
1016 #[serde(default)]
1018 pub offset: String,
1019 #[serde(default)]
1021 pub volume_orign: i64,
1022 #[serde(default)]
1024 pub price_type: String,
1025 #[serde(default)]
1027 pub limit_price: f64,
1028 #[serde(default)]
1030 pub time_condition: String,
1031 #[serde(default)]
1033 pub volume_condition: String,
1034 #[serde(default)]
1036 pub insert_date_time: i64,
1037 #[serde(default)]
1039 pub exchange_order_id: String,
1040 #[serde(default)]
1042 pub status: String,
1043 #[serde(default)]
1045 pub volume_left: i64,
1046 #[serde(default)]
1048 pub frozen_margin: f64,
1049 #[serde(default)]
1051 pub last_msg: String,
1052
1053 #[serde(skip_serializing_if = "Option::is_none")]
1054 #[serde(rename = "_epoch")]
1055 pub epoch: Option<i64>,
1056}
1057
1058impl Order {
1059 pub fn volume(&self) -> i64 {
1061 self.volume_orign
1062 }
1063
1064 pub fn price(&self) -> f64 {
1066 self.limit_price
1067 }
1068}
1069
1070#[derive(Debug, Clone, Serialize, Deserialize)]
1072pub struct Trade {
1073 #[serde(default)]
1075 pub seqno: i64,
1076 #[serde(default)]
1078 pub user_id: String,
1079 #[serde(default)]
1081 pub trade_id: String,
1082 #[serde(default)]
1084 pub exchange_id: String,
1085 #[serde(default)]
1087 pub instrument_id: String,
1088 #[serde(default)]
1090 pub order_id: String,
1091 #[serde(default)]
1093 pub exchange_trade_id: String,
1094 #[serde(default)]
1096 pub direction: String,
1097 #[serde(default)]
1099 pub offset: String,
1100 #[serde(default)]
1102 pub volume: i64,
1103 #[serde(default)]
1105 pub price: f64,
1106 #[serde(default)]
1108 pub trade_date_time: i64,
1109 #[serde(default)]
1111 pub commission: f64,
1112
1113 #[serde(skip_serializing_if = "Option::is_none")]
1114 #[serde(rename = "_epoch")]
1115 pub epoch: Option<i64>,
1116}
1117
1118#[derive(Debug, Clone, Serialize, Deserialize)]
1120pub struct NotifyEvent {
1121 pub code: String,
1123 pub level: String,
1125 pub r#type: String,
1127 pub content: String,
1129 pub bid: String,
1131 pub user_id: String,
1133}
1134
1135#[derive(Debug, Clone, Serialize, Deserialize)]
1137pub struct Notification {
1138 pub code: String,
1140 pub level: String,
1142 pub r#type: String,
1144 pub content: String,
1146 pub bid: String,
1148 pub user_id: String,
1150}
1151
1152#[derive(Debug, Clone)]
1154pub struct PositionUpdate {
1155 pub symbol: String,
1157 pub position: Position,
1159}
1160
1161#[derive(Debug, Clone)]
1163pub struct InsertOrderRequest {
1164 pub symbol: String,
1166 pub exchange_id: Option<String>,
1168 pub instrument_id: Option<String>,
1170 pub direction: String,
1172 pub offset: String,
1174 pub price_type: String,
1176 pub limit_price: f64,
1178 pub volume: i64,
1180}
1181
1182impl InsertOrderRequest {
1183 pub fn get_exchange_id(&self) -> String {
1185 if let Some(ref exchange) = self.exchange_id {
1186 return exchange.clone();
1187 }
1188 if let Some(dot_pos) = self.symbol.find('.') {
1190 self.symbol[..dot_pos].to_string()
1191 } else {
1192 String::new()
1193 }
1194 }
1195
1196 pub fn get_instrument_id(&self) -> String {
1198 if let Some(ref instrument) = self.instrument_id {
1199 return instrument.clone();
1200 }
1201 if let Some(dot_pos) = self.symbol.find('.') {
1203 self.symbol[dot_pos + 1..].to_string()
1204 } else {
1205 self.symbol.clone()
1206 }
1207 }
1208}
1209
1210pub const DIRECTION_BUY: &str = "BUY";
1214pub const DIRECTION_SELL: &str = "SELL";
1216
1217pub const OFFSET_OPEN: &str = "OPEN";
1219pub const OFFSET_CLOSE: &str = "CLOSE";
1221pub const OFFSET_CLOSETODAY: &str = "CLOSETODAY";
1223
1224pub const PRICE_TYPE_LIMIT: &str = "LIMIT";
1226pub const PRICE_TYPE_ANY: &str = "ANY";
1228
1229pub const ORDER_STATUS_ALIVE: &str = "ALIVE";
1231pub const ORDER_STATUS_FINISHED: &str = "FINISHED";
1233
1234#[derive(Debug, Clone)]
1238pub struct SeriesOptions {
1239 pub symbols: Vec<String>,
1241 pub duration: i64,
1243 pub view_width: usize,
1245 pub chart_id: Option<String>,
1247 pub left_kline_id: Option<i64>,
1249 pub focus_datetime: Option<DateTime<Utc>>,
1251 pub focus_position: Option<i32>,
1253}
1254
1255impl Default for SeriesOptions {
1256 fn default() -> Self {
1257 SeriesOptions {
1258 symbols: Vec::new(),
1259 duration: 0,
1260 view_width: 10000,
1261 chart_id: None,
1262 left_kline_id: None,
1263 focus_datetime: None,
1264 focus_position: None,
1265 }
1266 }
1267}