longport/trade/
push_types.rs
1use std::str::FromStr;
2
3use longport_proto::trade::Notification;
4use prost::Message;
5use rust_decimal::Decimal;
6use serde::Deserialize;
7use strum_macros::{Display, EnumString};
8use time::OffsetDateTime;
9
10use crate::{
11 Error, Result, serde_utils,
12 trade::{OrderSide, OrderStatus, OrderTag, OrderType, TriggerStatus, cmd_code},
13};
14
15#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
17pub enum TopicType {
18 #[strum(serialize = "private")]
20 Private,
21}
22
23#[derive(Debug, Deserialize)]
25pub struct PushOrderChanged {
26 pub side: OrderSide,
28 pub stock_name: String,
30 pub submitted_quantity: Decimal,
32 pub symbol: String,
34 pub order_type: OrderType,
36 pub submitted_price: Decimal,
38 pub executed_quantity: Decimal,
40 #[serde(with = "serde_utils::decimal_opt_0_is_none")]
42 pub executed_price: Option<Decimal>,
43 pub order_id: String,
45 pub currency: String,
47 pub status: OrderStatus,
49 #[serde(
51 serialize_with = "time::serde::rfc3339::serialize",
52 deserialize_with = "serde_utils::timestamp::deserialize"
53 )]
54 pub submitted_at: OffsetDateTime,
55 #[serde(
57 serialize_with = "time::serde::rfc3339::serialize",
58 deserialize_with = "serde_utils::timestamp::deserialize"
59 )]
60 pub updated_at: OffsetDateTime,
61 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
63 pub trigger_price: Option<Decimal>,
64 pub msg: String,
66 pub tag: OrderTag,
68 #[serde(with = "serde_utils::trigger_status")]
70 pub trigger_status: Option<TriggerStatus>,
71 #[serde(
73 deserialize_with = "serde_utils::timestamp_opt::deserialize",
74 serialize_with = "serde_utils::rfc3339_opt::serialize"
75 )]
76 pub trigger_at: Option<OffsetDateTime>,
77 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
79 pub trailing_amount: Option<Decimal>,
80 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
82 pub trailing_percent: Option<Decimal>,
83 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
85 pub limit_offset: Option<Decimal>,
86 pub account_no: String,
88 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
90 pub last_share: Option<Decimal>,
91 #[serde(with = "serde_utils::decimal_opt_empty_is_none")]
93 pub last_price: Option<Decimal>,
94 pub remark: String,
96}
97
98#[derive(Debug, Deserialize)]
100#[serde(tag = "event", content = "data")]
101pub enum PushEvent {
102 #[serde(rename = "order_changed_lb")]
104 OrderChanged(PushOrderChanged),
105}
106
107impl PushEvent {
108 pub(crate) fn parse(command_code: u8, data: &[u8]) -> Result<Option<PushEvent>> {
109 if command_code == cmd_code::PUSH_NOTIFICATION {
110 let notification = Notification::decode(data)?;
111 if let Ok(TopicType::Private) = TopicType::from_str(¬ification.topic) {
112 Ok(Some(serde_json::from_slice::<PushEvent>(
113 ¬ification.data,
114 )?))
115 } else {
116 Ok(None)
117 }
118 } else {
119 Err(Error::UnknownCommand(command_code))
120 }
121 }
122}