1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum BlockTradeRole {
12 Maker,
14 Taker,
16}
17
18impl std::fmt::Display for BlockTradeRole {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Self::Maker => write!(f, "maker"),
22 Self::Taker => write!(f, "taker"),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
29#[serde(rename_all = "lowercase")]
30pub enum TradeDirection {
31 Buy,
33 Sell,
35}
36
37impl std::fmt::Display for TradeDirection {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Self::Buy => write!(f, "buy"),
41 Self::Sell => write!(f, "sell"),
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50pub struct BlockTradeItem {
51 pub instrument_name: String,
53 pub price: f64,
55 #[serde(skip_serializing_if = "Option::is_none")]
58 pub amount: Option<f64>,
59 pub direction: TradeDirection,
61}
62
63impl BlockTradeItem {
64 #[must_use]
73 pub fn new(
74 instrument_name: impl Into<String>,
75 price: f64,
76 amount: Option<f64>,
77 direction: TradeDirection,
78 ) -> Self {
79 Self {
80 instrument_name: instrument_name.into(),
81 price,
82 amount,
83 direction,
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
90pub struct ExecuteBlockTradeRequest {
91 pub timestamp: u64,
93 pub nonce: String,
95 pub role: BlockTradeRole,
97 pub trades: Vec<BlockTradeItem>,
99 pub counterparty_signature: String,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct VerifyBlockTradeRequest {
106 pub timestamp: u64,
108 pub nonce: String,
110 pub role: BlockTradeRole,
112 pub trades: Vec<BlockTradeItem>,
114}
115
116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118pub struct SimulateBlockTradeRequest {
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub role: Option<BlockTradeRole>,
122 pub trades: Vec<BlockTradeItem>,
124}
125
126#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
128pub struct GetBlockTradesRequest {
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub currency: Option<String>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub count: Option<u32>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub continuation: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub start_timestamp: Option<u64>,
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub end_timestamp: Option<u64>,
144}
145
146#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
148pub struct GetBlockTradeRequestsParams {
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub broker_code: Option<String>,
152}
153
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
156pub struct BlockTradeTradeInfo {
157 pub trade_id: String,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub trade_seq: Option<u64>,
162 pub timestamp: u64,
164 #[serde(skip_serializing_if = "Option::is_none")]
166 pub tick_direction: Option<i32>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub state: Option<String>,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub reduce_only: Option<bool>,
173 pub price: f64,
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub post_only: Option<bool>,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub order_type: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub order_id: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub matching_id: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
189 pub mark_price: Option<f64>,
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub liquidity: Option<String>,
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub iv: Option<f64>,
196 pub instrument_name: String,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub index_price: Option<f64>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub fee_currency: Option<String>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub fee: Option<f64>,
207 pub direction: TradeDirection,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub block_trade_id: Option<String>,
212 pub amount: f64,
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub underlying_price: Option<f64>,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub api: Option<bool>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub advanced: Option<String>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub label: Option<String>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub mmp: Option<bool>,
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub quote_id: Option<String>,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub combo_id: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub profit_loss: Option<f64>,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub contracts: Option<f64>,
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub block_rfq_quote_id: Option<u64>,
244}
245
246#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
248pub struct BlockTrade {
249 pub id: String,
251 pub timestamp: u64,
253 pub trades: Vec<BlockTradeTradeInfo>,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub app_name: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub broker_code: Option<String>,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub broker_name: Option<String>,
264}
265
266#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
268pub struct BlockTradeResult {
269 pub id: String,
271 pub timestamp: u64,
273 pub trades: Vec<BlockTradeTradeInfo>,
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
279pub struct BlockTradeSignature {
280 pub signature: String,
282}
283
284#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
286pub struct BlockTradeRequest {
287 pub timestamp: u64,
289 pub nonce: String,
291 pub role: BlockTradeRole,
293 #[serde(skip_serializing_if = "Option::is_none")]
295 pub trades: Option<Vec<BlockTradeItem>>,
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub broker_code: Option<String>,
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub counterparty_user_id: Option<u64>,
302 #[serde(skip_serializing_if = "Option::is_none")]
304 pub state: Option<String>,
305}
306
307#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309pub struct GetBlockTradesResponse {
310 pub block_trades: Vec<BlockTrade>,
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub continuation: Option<String>,
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn test_block_trade_role_serialization() {
323 assert_eq!(
324 serde_json::to_string(&BlockTradeRole::Maker).unwrap(),
325 r#""maker""#
326 );
327 assert_eq!(
328 serde_json::to_string(&BlockTradeRole::Taker).unwrap(),
329 r#""taker""#
330 );
331 }
332
333 #[test]
334 fn test_block_trade_role_deserialization() {
335 assert_eq!(
336 serde_json::from_str::<BlockTradeRole>(r#""maker""#).unwrap(),
337 BlockTradeRole::Maker
338 );
339 assert_eq!(
340 serde_json::from_str::<BlockTradeRole>(r#""taker""#).unwrap(),
341 BlockTradeRole::Taker
342 );
343 }
344
345 #[test]
346 fn test_trade_direction_serialization() {
347 assert_eq!(
348 serde_json::to_string(&TradeDirection::Buy).unwrap(),
349 r#""buy""#
350 );
351 assert_eq!(
352 serde_json::to_string(&TradeDirection::Sell).unwrap(),
353 r#""sell""#
354 );
355 }
356
357 #[test]
358 fn test_block_trade_item_serialization() {
359 let item = BlockTradeItem::new("BTC-PERPETUAL", 50000.0, Some(100.0), TradeDirection::Buy);
360 let json = serde_json::to_string(&item).unwrap();
361 assert!(json.contains("BTC-PERPETUAL"));
362 assert!(json.contains("50000"));
363 assert!(json.contains("buy"));
364 }
365
366 #[test]
367 fn test_execute_block_trade_request_serialization() {
368 let request = ExecuteBlockTradeRequest {
369 timestamp: 1565172650935,
370 nonce: "test_nonce".to_string(),
371 role: BlockTradeRole::Maker,
372 trades: vec![BlockTradeItem::new(
373 "BTC-PERPETUAL",
374 50000.0,
375 Some(100.0),
376 TradeDirection::Buy,
377 )],
378 counterparty_signature: "sig123".to_string(),
379 };
380 let json = serde_json::to_string(&request).unwrap();
381 assert!(json.contains("1565172650935"));
382 assert!(json.contains("test_nonce"));
383 assert!(json.contains("maker"));
384 assert!(json.contains("sig123"));
385 }
386
387 #[test]
388 fn test_verify_block_trade_request_serialization() {
389 let request = VerifyBlockTradeRequest {
390 timestamp: 1565172650935,
391 nonce: "test_nonce".to_string(),
392 role: BlockTradeRole::Taker,
393 trades: vec![BlockTradeItem::new(
394 "ETH-PERPETUAL",
395 3000.0,
396 Some(50.0),
397 TradeDirection::Sell,
398 )],
399 };
400 let json = serde_json::to_string(&request).unwrap();
401 assert!(json.contains("taker"));
402 assert!(json.contains("ETH-PERPETUAL"));
403 }
404
405 #[test]
406 fn test_block_trade_signature_deserialization() {
407 let json = r#"{"signature":"1565172710935.1ESE83qh.abc123"}"#;
408 let sig: BlockTradeSignature = serde_json::from_str(json).unwrap();
409 assert_eq!(sig.signature, "1565172710935.1ESE83qh.abc123");
410 }
411
412 #[test]
413 fn test_block_trade_deserialization() {
414 let json = r#"{
415 "id": "61",
416 "timestamp": 1565089523720,
417 "trades": [
418 {
419 "trade_id": "92437",
420 "timestamp": 1565089523719,
421 "price": 0.0001,
422 "instrument_name": "BTC-9AUG19-10250-C",
423 "direction": "sell",
424 "amount": 10
425 }
426 ],
427 "broker_code": "ABC123"
428 }"#;
429 let trade: BlockTrade = serde_json::from_str(json).unwrap();
430 assert_eq!(trade.id, "61");
431 assert_eq!(trade.trades.len(), 1);
432 assert_eq!(trade.broker_code, Some("ABC123".to_string()));
433 }
434
435 #[test]
436 fn test_get_block_trades_request_default() {
437 let request = GetBlockTradesRequest::default();
438 assert!(request.currency.is_none());
439 assert!(request.count.is_none());
440 assert!(request.continuation.is_none());
441 }
442
443 #[test]
444 fn test_simulate_block_trade_request_serialization() {
445 let request = SimulateBlockTradeRequest {
446 role: Some(BlockTradeRole::Maker),
447 trades: vec![BlockTradeItem::new(
448 "BTC-PERPETUAL",
449 50000.0,
450 Some(40.0),
451 TradeDirection::Buy,
452 )],
453 };
454 let json = serde_json::to_string(&request).unwrap();
455 assert!(json.contains("maker"));
456 assert!(json.contains("BTC-PERPETUAL"));
457 }
458}