use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BlockTradeRole {
Maker,
Taker,
}
impl std::fmt::Display for BlockTradeRole {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Maker => write!(f, "maker"),
Self::Taker => write!(f, "taker"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TradeDirection {
Buy,
Sell,
}
impl std::fmt::Display for TradeDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Buy => write!(f, "buy"),
Self::Sell => write!(f, "sell"),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlockTradeItem {
pub instrument_name: String,
pub price: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub amount: Option<f64>,
pub direction: TradeDirection,
}
impl BlockTradeItem {
#[must_use]
pub fn new(
instrument_name: impl Into<String>,
price: f64,
amount: Option<f64>,
direction: TradeDirection,
) -> Self {
Self {
instrument_name: instrument_name.into(),
price,
amount,
direction,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExecuteBlockTradeRequest {
pub timestamp: u64,
pub nonce: String,
pub role: BlockTradeRole,
pub trades: Vec<BlockTradeItem>,
pub counterparty_signature: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VerifyBlockTradeRequest {
pub timestamp: u64,
pub nonce: String,
pub role: BlockTradeRole,
pub trades: Vec<BlockTradeItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SimulateBlockTradeRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<BlockTradeRole>,
pub trades: Vec<BlockTradeItem>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct GetBlockTradesRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub continuation: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_timestamp: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_timestamp: Option<u64>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct GetBlockTradeRequestsParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub broker_code: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlockTradeTradeInfo {
pub trade_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub trade_seq: Option<u64>,
pub timestamp: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub tick_direction: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reduce_only: Option<bool>,
pub price: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub matching_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mark_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub liquidity: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iv: Option<f64>,
pub instrument_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fee_currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fee: Option<f64>,
pub direction: TradeDirection,
#[serde(skip_serializing_if = "Option::is_none")]
pub block_trade_id: Option<String>,
pub amount: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub underlying_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub api: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub advanced: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mmp: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quote_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub combo_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub profit_loss: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contracts: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub block_rfq_quote_id: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlockTrade {
pub id: String,
pub timestamp: u64,
pub trades: Vec<BlockTradeTradeInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub app_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub broker_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub broker_name: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlockTradeResult {
pub id: String,
pub timestamp: u64,
pub trades: Vec<BlockTradeTradeInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BlockTradeSignature {
pub signature: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlockTradeRequest {
pub timestamp: u64,
pub nonce: String,
pub role: BlockTradeRole,
#[serde(skip_serializing_if = "Option::is_none")]
pub trades: Option<Vec<BlockTradeItem>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub broker_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub counterparty_user_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GetBlockTradesResponse {
pub block_trades: Vec<BlockTrade>,
#[serde(skip_serializing_if = "Option::is_none")]
pub continuation: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_block_trade_role_serialization() {
assert_eq!(
serde_json::to_string(&BlockTradeRole::Maker).unwrap(),
r#""maker""#
);
assert_eq!(
serde_json::to_string(&BlockTradeRole::Taker).unwrap(),
r#""taker""#
);
}
#[test]
fn test_block_trade_role_deserialization() {
assert_eq!(
serde_json::from_str::<BlockTradeRole>(r#""maker""#).unwrap(),
BlockTradeRole::Maker
);
assert_eq!(
serde_json::from_str::<BlockTradeRole>(r#""taker""#).unwrap(),
BlockTradeRole::Taker
);
}
#[test]
fn test_trade_direction_serialization() {
assert_eq!(
serde_json::to_string(&TradeDirection::Buy).unwrap(),
r#""buy""#
);
assert_eq!(
serde_json::to_string(&TradeDirection::Sell).unwrap(),
r#""sell""#
);
}
#[test]
fn test_block_trade_item_serialization() {
let item = BlockTradeItem::new("BTC-PERPETUAL", 50000.0, Some(100.0), TradeDirection::Buy);
let json = serde_json::to_string(&item).unwrap();
assert!(json.contains("BTC-PERPETUAL"));
assert!(json.contains("50000"));
assert!(json.contains("buy"));
}
#[test]
fn test_execute_block_trade_request_serialization() {
let request = ExecuteBlockTradeRequest {
timestamp: 1565172650935,
nonce: "test_nonce".to_string(),
role: BlockTradeRole::Maker,
trades: vec![BlockTradeItem::new(
"BTC-PERPETUAL",
50000.0,
Some(100.0),
TradeDirection::Buy,
)],
counterparty_signature: "sig123".to_string(),
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("1565172650935"));
assert!(json.contains("test_nonce"));
assert!(json.contains("maker"));
assert!(json.contains("sig123"));
}
#[test]
fn test_verify_block_trade_request_serialization() {
let request = VerifyBlockTradeRequest {
timestamp: 1565172650935,
nonce: "test_nonce".to_string(),
role: BlockTradeRole::Taker,
trades: vec![BlockTradeItem::new(
"ETH-PERPETUAL",
3000.0,
Some(50.0),
TradeDirection::Sell,
)],
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("taker"));
assert!(json.contains("ETH-PERPETUAL"));
}
#[test]
fn test_block_trade_signature_deserialization() {
let json = r#"{"signature":"1565172710935.1ESE83qh.abc123"}"#;
let sig: BlockTradeSignature = serde_json::from_str(json).unwrap();
assert_eq!(sig.signature, "1565172710935.1ESE83qh.abc123");
}
#[test]
fn test_block_trade_deserialization() {
let json = r#"{
"id": "61",
"timestamp": 1565089523720,
"trades": [
{
"trade_id": "92437",
"timestamp": 1565089523719,
"price": 0.0001,
"instrument_name": "BTC-9AUG19-10250-C",
"direction": "sell",
"amount": 10
}
],
"broker_code": "ABC123"
}"#;
let trade: BlockTrade = serde_json::from_str(json).unwrap();
assert_eq!(trade.id, "61");
assert_eq!(trade.trades.len(), 1);
assert_eq!(trade.broker_code, Some("ABC123".to_string()));
}
#[test]
fn test_get_block_trades_request_default() {
let request = GetBlockTradesRequest::default();
assert!(request.currency.is_none());
assert!(request.count.is_none());
assert!(request.continuation.is_none());
}
#[test]
fn test_simulate_block_trade_request_serialization() {
let request = SimulateBlockTradeRequest {
role: Some(BlockTradeRole::Maker),
trades: vec![BlockTradeItem::new(
"BTC-PERPETUAL",
50000.0,
Some(40.0),
TradeDirection::Buy,
)],
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("maker"));
assert!(json.contains("BTC-PERPETUAL"));
}
}