Skip to main content

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