longport/quote/
push_types.rs

1use longport_proto::quote::{self, Period, PushQuoteTag, TradeStatus};
2use prost::Message;
3use rust_decimal::Decimal;
4use time::OffsetDateTime;
5
6use crate::{
7    Error, Result,
8    quote::{Brokers, Candlestick, Depth, Trade, TradeSession, cmd_code},
9};
10
11/// Quote message
12#[derive(Debug, Clone)]
13pub struct PushQuote {
14    /// Latest price
15    pub last_done: Decimal,
16    /// Open
17    pub open: Decimal,
18    /// High
19    pub high: Decimal,
20    /// Low
21    pub low: Decimal,
22    /// Time of latest price
23    pub timestamp: OffsetDateTime,
24    /// Volume
25    pub volume: i64,
26    /// Turnover
27    pub turnover: Decimal,
28    /// Security trading status
29    pub trade_status: TradeStatus,
30    /// Trade session
31    pub trade_session: TradeSession,
32    /// Increase volume between pushes
33    pub current_volume: i64,
34    /// Increase turnover between pushes
35    pub current_turnover: Decimal,
36}
37
38impl Default for PushQuote {
39    fn default() -> Self {
40        Self {
41            last_done: Default::default(),
42            open: Default::default(),
43            high: Default::default(),
44            low: Default::default(),
45            timestamp: OffsetDateTime::from_unix_timestamp(0).unwrap(),
46            volume: Default::default(),
47            turnover: Default::default(),
48            trade_status: Default::default(),
49            trade_session: Default::default(),
50            current_volume: Default::default(),
51            current_turnover: Default::default(),
52        }
53    }
54}
55
56/// Depth message
57#[derive(Debug)]
58pub struct PushDepth {
59    /// Ask depth
60    pub asks: Vec<Depth>,
61    /// Bid depth
62    pub bids: Vec<Depth>,
63}
64
65/// Brokers message
66#[derive(Debug)]
67pub struct PushBrokers {
68    /// Ask brokers
69    pub ask_brokers: Vec<Brokers>,
70    /// Bid brokers
71    pub bid_brokers: Vec<Brokers>,
72}
73
74/// Trades message
75#[derive(Debug)]
76pub struct PushTrades {
77    /// Trades data
78    pub trades: Vec<Trade>,
79}
80
81/// Candlestick updated message
82#[derive(Debug, Copy, Clone)]
83pub struct PushCandlestick {
84    /// Period type
85    pub period: Period,
86    /// Candlestick
87    pub candlestick: Candlestick,
88    /// Is confirmed
89    pub is_confirmed: bool,
90}
91
92/// Push event detail
93#[derive(Debug)]
94pub enum PushEventDetail {
95    /// Quote
96    Quote(PushQuote),
97    /// Depth
98    Depth(PushDepth),
99    /// Brokers
100    Brokers(PushBrokers),
101    /// Trade
102    Trade(PushTrades),
103    /// Candlestick
104    Candlestick(PushCandlestick),
105}
106
107/// Push event
108#[derive(Debug)]
109pub struct PushEvent {
110    #[allow(dead_code)]
111    pub(crate) sequence: i64,
112    /// Security code
113    pub symbol: String,
114    /// Event detail
115    pub detail: PushEventDetail,
116}
117
118impl PushEvent {
119    pub(crate) fn parse(
120        command_code: u8,
121        data: &[u8],
122    ) -> Result<(PushEvent, Option<PushQuoteTag>)> {
123        match command_code {
124            cmd_code::PUSH_REALTIME_QUOTE => {
125                parse_push_quote(data).map(|(event, tag)| (event, Some(tag)))
126            }
127            cmd_code::PUSH_REALTIME_DEPTH => parse_push_depth(data).map(|event| (event, None)),
128            cmd_code::PUSH_REALTIME_BROKERS => parse_push_brokers(data).map(|event| (event, None)),
129            cmd_code::PUSH_REALTIME_TRADES => parse_push_trade(data).map(|event| (event, None)),
130            _ => Err(Error::UnknownCommand(command_code)),
131        }
132    }
133}
134
135fn parse_push_quote(data: &[u8]) -> Result<(PushEvent, PushQuoteTag)> {
136    let push_quote = quote::PushQuote::decode(data)?;
137    Ok((
138        PushEvent {
139            symbol: push_quote.symbol,
140            sequence: push_quote.sequence,
141            detail: PushEventDetail::Quote(PushQuote {
142                last_done: push_quote.last_done.parse().unwrap_or_default(),
143                open: push_quote.open.parse().unwrap_or_default(),
144                high: push_quote.high.parse().unwrap_or_default(),
145                low: push_quote.low.parse().unwrap_or_default(),
146                timestamp: OffsetDateTime::from_unix_timestamp(push_quote.timestamp)
147                    .map_err(|err| Error::parse_field_error("timestamp", err))?,
148                volume: push_quote.volume,
149                turnover: push_quote.turnover.parse().unwrap_or_default(),
150                trade_status: TradeStatus::try_from(push_quote.trade_status).unwrap_or_default(),
151                trade_session: longport_proto::quote::TradeSession::try_from(
152                    push_quote.trade_session,
153                )
154                .unwrap_or_default()
155                .into(),
156                current_volume: push_quote.current_volume,
157                current_turnover: push_quote.current_turnover.parse().unwrap_or_default(),
158            }),
159        },
160        PushQuoteTag::try_from(push_quote.tag).unwrap_or_default(),
161    ))
162}
163
164fn parse_push_depth(data: &[u8]) -> Result<PushEvent> {
165    let push_depth = quote::PushDepth::decode(data)?;
166    Ok(PushEvent {
167        symbol: push_depth.symbol,
168        sequence: push_depth.sequence,
169        detail: PushEventDetail::Depth(PushDepth {
170            asks: push_depth
171                .ask
172                .into_iter()
173                .map(TryInto::try_into)
174                .collect::<Result<Vec<_>>>()?,
175            bids: push_depth
176                .bid
177                .into_iter()
178                .map(TryInto::try_into)
179                .collect::<Result<Vec<_>>>()?,
180        }),
181    })
182}
183
184fn parse_push_brokers(data: &[u8]) -> Result<PushEvent> {
185    let push_brokers = quote::PushBrokers::decode(data)?;
186
187    Ok(PushEvent {
188        symbol: push_brokers.symbol,
189        sequence: push_brokers.sequence,
190        detail: PushEventDetail::Brokers(PushBrokers {
191            ask_brokers: push_brokers
192                .ask_brokers
193                .into_iter()
194                .map(Into::into)
195                .collect(),
196            bid_brokers: push_brokers
197                .bid_brokers
198                .into_iter()
199                .map(Into::into)
200                .collect(),
201        }),
202    })
203}
204
205fn parse_push_trade(data: &[u8]) -> Result<PushEvent> {
206    let push_trades = quote::PushTrade::decode(data)?;
207    Ok(PushEvent {
208        symbol: push_trades.symbol,
209        sequence: push_trades.sequence,
210        detail: PushEventDetail::Trade(PushTrades {
211            trades: push_trades
212                .trade
213                .into_iter()
214                .map(TryInto::try_into)
215                .collect::<Result<Vec<_>>>()?,
216        }),
217    })
218}