barter_data/exchange/bitmex/
trade.rs1use crate::{
2 event::{MarketEvent, MarketIter},
3 exchange::bitmex::message::BitmexMessage,
4 subscription::trade::PublicTrade,
5};
6use barter_instrument::{Side, exchange::ExchangeId};
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10pub type BitmexTrade = BitmexMessage<BitmexTradeInner>;
12
13#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
38pub struct BitmexTradeInner {
39 pub timestamp: DateTime<Utc>,
40 pub symbol: String,
41 pub side: Side,
42 #[serde(rename = "size")]
43 pub amount: f64,
44 pub price: f64,
45
46 #[serde(rename = "trdMatchID")]
47 pub id: String,
48}
49
50impl<InstrumentKey: Clone> From<(ExchangeId, InstrumentKey, BitmexTrade)>
51 for MarketIter<InstrumentKey, PublicTrade>
52{
53 fn from((exchange, instrument, trades): (ExchangeId, InstrumentKey, BitmexTrade)) -> Self {
54 Self(
55 trades
56 .data
57 .into_iter()
58 .map(|trade| {
59 Ok(MarketEvent {
60 time_exchange: trade.timestamp,
61 time_received: Utc::now(),
62 exchange,
63 instrument: instrument.clone(),
64 kind: PublicTrade {
65 id: trade.id,
66 price: trade.price,
67 amount: trade.amount,
68 side: trade.side,
69 },
70 })
71 })
72 .collect(),
73 )
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 mod de {
82 use super::*;
83 use barter_integration::error::SocketError;
84 use chrono::{Duration, TimeZone};
85
86 #[test]
87 fn test_bitmex_trade() {
88 struct TestCase {
89 input: &'static str,
90 expected: Result<BitmexTradeInner, SocketError>,
91 }
92
93 let tests = vec![
94 TestCase {
96 input: r#"
97 {
98 "timestamp": "2023-02-18T09:27:59.701Z",
99 "symbol": "XBTUSD",
100 "side": "Sell",
101 "size": 200,
102 "price": 24564.5,
103 "tickDirection": "MinusTick",
104 "trdMatchID": "31e50cb7-e005-a44e-f354-86e88dff52eb",
105 "grossValue": 814184,
106 "homeNotional": 0.00814184,
107 "foreignNotional": 200,
108 "trdType": "Regular"
109 }
110 "#,
111 expected: Ok(BitmexTradeInner {
112 timestamp: Utc.with_ymd_and_hms(2023, 2, 18, 9, 27, 59).unwrap()
113 + Duration::milliseconds(701),
114 symbol: "XBTUSD".to_string(),
115 side: Side::Sell,
116 amount: 200.0,
117 price: 24564.5,
118 id: "31e50cb7-e005-a44e-f354-86e88dff52eb".to_string(),
119 }),
120 },
121 ];
122
123 for (index, test) in tests.into_iter().enumerate() {
124 let actual = serde_json::from_str::<BitmexTradeInner>(test.input);
125 match (actual, test.expected) {
126 (Ok(actual), Ok(expected)) => {
127 assert_eq!(actual, expected, "TC{} failed", index)
128 }
129 (Err(_), Err(_)) => {
130 }
132 (actual, expected) => {
133 panic!(
135 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
136 );
137 }
138 }
139 }
140 }
141
142 #[test]
143 fn test_bitmex_trade_payload() {
144 struct TestCase {
145 input: &'static str,
146 expected: Result<BitmexTrade, SocketError>,
147 }
148
149 let tests = vec![
150 TestCase {
152 input: r#"
153 {
154 "table": "trade",
155 "action": "insert",
156 "data": [
157 {
158 "timestamp": "2023-02-18T09:27:59.701Z",
159 "symbol": "XBTUSD",
160 "side": "Sell",
161 "size": 200,
162 "price": 24564.5,
163 "tickDirection": "MinusTick",
164 "trdMatchID": "31e50cb7-e005-a44e-f354-86e88dff52eb",
165 "grossValue": 814184,
166 "homeNotional": 0.00814184,
167 "foreignNotional": 200,
168 "trdType": "Regular"
169 }
170 ]
171 }
172 "#,
173 expected: Ok(BitmexTrade {
174 table: "trade".to_string(),
175 data: vec![BitmexTradeInner {
176 timestamp: Utc.with_ymd_and_hms(2023, 2, 18, 9, 27, 59).unwrap()
177 + Duration::milliseconds(701),
178 symbol: "XBTUSD".to_string(),
179 side: Side::Sell,
180 amount: 200.0,
181 price: 24564.5,
182 id: "31e50cb7-e005-a44e-f354-86e88dff52eb".to_string(),
183 }],
184 }),
185 },
186 ];
187
188 for (index, test) in tests.into_iter().enumerate() {
189 let actual = serde_json::from_str::<BitmexTrade>(test.input);
190 match (actual, test.expected) {
191 (Ok(actual), Ok(expected)) => {
192 assert_eq!(actual, expected, "TC{} failed", index)
193 }
194 (Err(_), Err(_)) => {
195 }
197 (actual, expected) => {
198 panic!(
200 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
201 );
202 }
203 }
204 }
205 }
206 }
207}