use std::str::FromStr;
use longport_proto::trade::Notification;
use prost::Message;
use rust_decimal::Decimal;
use serde::Deserialize;
use strum_macros::{Display, EnumString};
use time::OffsetDateTime;
use crate::{
serde_utils,
trade::{cmd_code, OrderSide, OrderStatus, OrderTag, OrderType, TriggerStatus},
Error, Result,
};
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, EnumString, Display)]
pub enum TopicType {
#[strum(serialize = "private")]
Private,
}
#[derive(Debug, Deserialize)]
pub struct PushOrderChanged {
pub side: OrderSide,
pub stock_name: String,
pub submitted_quantity: Decimal,
pub symbol: String,
pub order_type: OrderType,
pub submitted_price: Decimal,
pub executed_quantity: Decimal,
#[serde(with = "serde_utils::decimal_opt_0_is_none")]
pub executed_price: Option<Decimal>,
pub order_id: String,
pub currency: String,
pub status: OrderStatus,
#[serde(with = "serde_utils::timestamp")]
pub submitted_at: OffsetDateTime,
#[serde(with = "serde_utils::timestamp")]
pub updated_at: OffsetDateTime,
#[serde(with = "serde_utils::decimal_opt_empty_is_none")]
pub trigger_price: Option<Decimal>,
pub msg: String,
pub tag: OrderTag,
#[serde(with = "serde_utils::trigger_status")]
pub trigger_status: Option<TriggerStatus>,
#[serde(with = "serde_utils::timestamp_opt")]
pub trigger_at: Option<OffsetDateTime>,
#[serde(with = "serde_utils::decimal_opt_empty_is_none")]
pub trailing_amount: Option<Decimal>,
#[serde(with = "serde_utils::decimal_opt_empty_is_none")]
pub trailing_percent: Option<Decimal>,
#[serde(with = "serde_utils::decimal_opt_empty_is_none")]
pub limit_offset: Option<Decimal>,
pub account_no: String,
#[serde(with = "serde_utils::decimal_opt_empty_is_none")]
pub last_share: Option<Decimal>,
#[serde(with = "serde_utils::decimal_opt_empty_is_none")]
pub last_price: Option<Decimal>,
pub remark: String,
}
#[derive(Debug, Deserialize)]
#[serde(tag = "event", content = "data")]
pub enum PushEvent {
#[serde(rename = "order_changed_lb")]
OrderChanged(PushOrderChanged),
}
impl PushEvent {
pub(crate) fn parse(command_code: u8, data: &[u8]) -> Result<Option<PushEvent>> {
if command_code == cmd_code::PUSH_NOTIFICATION {
let notification = Notification::decode(data)?;
if let Ok(TopicType::Private) = TopicType::from_str(¬ification.topic) {
Ok(Some(serde_json::from_slice::<PushEvent>(
¬ification.data,
)?))
} else {
Ok(None)
}
} else {
Err(Error::UnknownCommand(command_code))
}
}
}