longbridge/trade/
push_types.rs1use std::str::FromStr;
2
3use longbridge_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::{MultiLegInfo, 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 #[serde(default)]
99 pub multi_leg: Option<MultiLegInfo>,
100}
101
102#[derive(Debug, Default, Deserialize)]
108#[serde(default)]
109pub struct PushGridOrderChanged {
110 pub order_id: String,
112 pub status: String,
114 pub symbol: String,
116 pub suspend_reason: String,
118 pub submitted_base_price: String,
120 pub current_base_price: String,
122 pub upper_limit_price: String,
124 pub lower_limit_price: String,
126 pub trigger_price_type: i32,
128 pub trigger_quantity: String,
130 pub settlement_currency: String,
132 pub time_in_force: i32,
134 pub rth: i32,
136 pub grid_order_type_up: String,
138 pub grid_order_type_down: String,
140}
141
142#[derive(Debug, Deserialize)]
144#[serde(tag = "event", content = "data")]
145pub enum PushEvent {
146 #[serde(rename = "order_changed_lb")]
148 OrderChanged(PushOrderChanged),
149 #[serde(rename = "gridtrading_order")]
151 GridOrderChanged(PushGridOrderChanged),
152}
153
154impl PushEvent {
155 pub(crate) fn parse(command_code: u8, data: &[u8]) -> Result<Option<PushEvent>> {
156 if command_code == cmd_code::PUSH_NOTIFICATION {
157 let notification = Notification::decode(data)?;
158 if let Ok(TopicType::Private) = TopicType::from_str(¬ification.topic) {
159 Ok(Some(serde_json::from_slice::<PushEvent>(
160 ¬ification.data,
161 )?))
162 } else {
163 Ok(None)
164 }
165 } else {
166 Err(Error::UnknownCommand(command_code))
167 }
168 }
169}