pragma_common/entries/
trade.rs1#[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}