pragma_common/entries/
trade.rs

1#[cfg(feature = "proto")]
2use prost::Message;
3
4use crate::{instrument_type::InstrumentType, pair::Pair, trading::Side};
5#[cfg(feature = "proto")]
6use crate::{ProtoDeserialize, ProtoSerialize};
7
8#[derive(Debug, Clone, PartialEq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(
11    feature = "borsh",
12    derive(borsh::BorshSerialize, borsh::BorshDeserialize)
13)]
14#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
15pub struct TradeEntry {
16    pub source: String,
17    pub instrument_type: InstrumentType,
18    pub pair: Pair,
19    pub trade_id: String,
20    pub side: TradeSide,
21    pub size: f64,
22    pub price: f64,
23    pub timestamp_ms: i64,
24}
25
26#[derive(Debug, Clone, PartialEq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(
29    feature = "borsh",
30    derive(borsh::BorshSerialize, borsh::BorshDeserialize)
31)]
32#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
33pub enum TradeSide {
34    Buy,
35    Sell,
36}
37
38impl From<TradeSide> for Side {
39    fn from(value: TradeSide) -> Self {
40        match value {
41            TradeSide::Buy => Self::Long,
42            TradeSide::Sell => Self::Short,
43        }
44    }
45}
46
47#[cfg(feature = "proto")]
48impl TradeEntry {
49    fn to_proto(&self) -> crate::schema::TradeEntry {
50        crate::schema::TradeEntry {
51            source: self.source.clone(),
52            instrument_type: match self.instrument_type {
53                InstrumentType::Spot => crate::schema::InstrumentType::Spot as i32,
54                InstrumentType::Perp => crate::schema::InstrumentType::Perp as i32,
55            },
56            pair: Some(crate::schema::Pair {
57                base: self.pair.base.clone(),
58                quote: self.pair.quote.clone(),
59            }),
60            trade_id: self.trade_id.clone(),
61            side: match self.side {
62                TradeSide::Buy => crate::schema::TradeSide::Buy as i32,
63                TradeSide::Sell => crate::schema::TradeSide::Sell as i32,
64            },
65            size: self.size,
66            price: self.price,
67            timestamp_ms: self.timestamp_ms,
68        }
69    }
70
71    fn from_proto(proto: crate::schema::TradeEntry) -> Result<Self, prost::DecodeError> {
72        let pair = proto
73            .pair
74            .ok_or_else(|| prost::DecodeError::new("Missing pair field in TradeEntry"))?;
75
76        let instrument_type = match proto.instrument_type {
77            x if x == crate::schema::InstrumentType::Spot as i32 => InstrumentType::Spot,
78            x if x == crate::schema::InstrumentType::Perp as i32 => InstrumentType::Perp,
79            _ => {
80                return Err(prost::DecodeError::new(format!(
81                    "Invalid instrument_type value: {}",
82                    proto.instrument_type,
83                )))
84            }
85        };
86
87        let side = match proto.side {
88            x if x == crate::schema::TradeSide::Buy as i32 => TradeSide::Buy,
89            x if x == crate::schema::TradeSide::Sell as i32 => TradeSide::Sell,
90            _ => {
91                return Err(prost::DecodeError::new(format!(
92                    "Invalid side value: {}",
93                    proto.side,
94                )))
95            }
96        };
97
98        Ok(TradeEntry {
99            source: proto.source,
100            instrument_type,
101            pair: Pair {
102                base: pair.base,
103                quote: pair.quote,
104            },
105            trade_id: proto.trade_id.clone(),
106            side,
107            size: proto.size,
108            price: proto.price,
109            timestamp_ms: proto.timestamp_ms,
110        })
111    }
112}
113
114#[cfg(feature = "proto")]
115impl ProtoSerialize for TradeEntry {
116    fn to_proto_bytes(&self) -> Vec<u8> {
117        let proto = self.to_proto();
118        let mut buf = Vec::new();
119        proto
120            .encode(&mut buf)
121            .expect("Failed to encode VolumeEntry to protobuf");
122        buf
123    }
124}
125
126#[cfg(feature = "proto")]
127impl ProtoDeserialize for TradeEntry {
128    fn from_proto_bytes(bytes: &[u8]) -> Result<Self, prost::DecodeError> {
129        let proto = crate::schema::TradeEntry::decode(bytes)?;
130        Self::from_proto(proto)
131    }
132}