use longport_proto::quote::{self, Period, PushQuoteTag, TradeSession, TradeStatus};
use prost::Message;
use rust_decimal::Decimal;
use time::OffsetDateTime;
use crate::{
quote::{cmd_code, Brokers, Candlestick, Depth, Trade},
Error, Result,
};
#[derive(Debug, Clone)]
pub struct PushQuote {
pub last_done: Decimal,
pub open: Decimal,
pub high: Decimal,
pub low: Decimal,
pub timestamp: OffsetDateTime,
pub volume: i64,
pub turnover: Decimal,
pub trade_status: TradeStatus,
pub trade_session: TradeSession,
}
impl Default for PushQuote {
fn default() -> Self {
Self {
last_done: Default::default(),
open: Default::default(),
high: Default::default(),
low: Default::default(),
timestamp: OffsetDateTime::from_unix_timestamp(0).unwrap(),
volume: Default::default(),
turnover: Default::default(),
trade_status: Default::default(),
trade_session: Default::default(),
}
}
}
#[derive(Debug)]
pub struct PushDepth {
pub asks: Vec<Depth>,
pub bids: Vec<Depth>,
}
#[derive(Debug)]
pub struct PushBrokers {
pub ask_brokers: Vec<Brokers>,
pub bid_brokers: Vec<Brokers>,
}
#[derive(Debug)]
pub struct PushTrades {
pub trades: Vec<Trade>,
}
#[derive(Debug, Copy, Clone)]
pub struct PushCandlestick {
pub period: Period,
pub candlestick: Candlestick,
}
#[derive(Debug)]
pub enum PushEventDetail {
Quote(PushQuote),
Depth(PushDepth),
Brokers(PushBrokers),
Trade(PushTrades),
Candlestick(PushCandlestick),
}
#[derive(Debug)]
pub struct PushEvent {
#[allow(dead_code)]
pub(crate) sequence: i64,
pub symbol: String,
pub detail: PushEventDetail,
}
impl PushEvent {
pub(crate) fn parse(
command_code: u8,
data: &[u8],
) -> Result<(PushEvent, Option<PushQuoteTag>)> {
match command_code {
cmd_code::PUSH_REALTIME_QUOTE => {
parse_push_quote(data).map(|(event, tag)| (event, Some(tag)))
}
cmd_code::PUSH_REALTIME_DEPTH => parse_push_depth(data).map(|event| (event, None)),
cmd_code::PUSH_REALTIME_BROKERS => parse_push_brokers(data).map(|event| (event, None)),
cmd_code::PUSH_REALTIME_TRADES => parse_push_trade(data).map(|event| (event, None)),
_ => Err(Error::UnknownCommand(command_code)),
}
}
}
fn parse_push_quote(data: &[u8]) -> Result<(PushEvent, PushQuoteTag)> {
let push_quote = quote::PushQuote::decode(data)?;
Ok((
PushEvent {
symbol: push_quote.symbol,
sequence: push_quote.sequence,
detail: PushEventDetail::Quote(PushQuote {
last_done: push_quote.last_done.parse().unwrap_or_default(),
open: push_quote.open.parse().unwrap_or_default(),
high: push_quote.high.parse().unwrap_or_default(),
low: push_quote.low.parse().unwrap_or_default(),
timestamp: OffsetDateTime::from_unix_timestamp(push_quote.timestamp)
.map_err(|err| Error::parse_field_error("timestamp", err))?,
volume: push_quote.volume,
turnover: push_quote.turnover.parse().unwrap_or_default(),
trade_status: TradeStatus::from_i32(push_quote.trade_status).unwrap_or_default(),
trade_session: TradeSession::from_i32(push_quote.trade_session).unwrap_or_default(),
}),
},
PushQuoteTag::from_i32(push_quote.tag).unwrap_or_default(),
))
}
fn parse_push_depth(data: &[u8]) -> Result<PushEvent> {
let push_depth = quote::PushDepth::decode(data)?;
Ok(PushEvent {
symbol: push_depth.symbol,
sequence: push_depth.sequence,
detail: PushEventDetail::Depth(PushDepth {
asks: push_depth
.ask
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>>>()?,
bids: push_depth
.bid
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>>>()?,
}),
})
}
fn parse_push_brokers(data: &[u8]) -> Result<PushEvent> {
let push_brokers = quote::PushBrokers::decode(data)?;
Ok(PushEvent {
symbol: push_brokers.symbol,
sequence: push_brokers.sequence,
detail: PushEventDetail::Brokers(PushBrokers {
ask_brokers: push_brokers
.ask_brokers
.into_iter()
.map(Into::into)
.collect(),
bid_brokers: push_brokers
.bid_brokers
.into_iter()
.map(Into::into)
.collect(),
}),
})
}
fn parse_push_trade(data: &[u8]) -> Result<PushEvent> {
let push_trades = quote::PushTrade::decode(data)?;
Ok(PushEvent {
symbol: push_trades.symbol,
sequence: push_trades.sequence,
detail: PushEventDetail::Trade(PushTrades {
trades: push_trades
.trade
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>>>()?,
}),
})
}