deribit_base/model/
trade.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 21/7/25
5******************************************************************************/
6
7use crate::model::{currency::Currency, instrument::InstrumentKind, order::OrderSide};
8use serde::{Deserialize, Serialize};
9
10/// Liquidity type enumeration
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub enum Liquidity {
13    /// Maker (provided liquidity)
14    #[serde(rename = "M")]
15    Maker,
16    /// Taker (consumed liquidity)
17    #[serde(rename = "T")]
18    Taker,
19    /// Mixed (both maker and taker in same trade)
20    #[serde(rename = "MT")]
21    Mixed,
22}
23
24/// Trade execution information
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Trade {
27    /// Unique trade identifier
28    pub trade_id: String,
29    /// Instrument name
30    pub instrument_name: String,
31    /// Order ID that generated this trade
32    pub order_id: String,
33    /// Trade direction (buy/sell)
34    pub direction: OrderSide,
35    /// Trade amount
36    pub amount: f64,
37    /// Execution price
38    pub price: f64,
39    /// Trade timestamp
40    pub timestamp: i64,
41    /// Fee amount
42    pub fee: f64,
43    /// Fee currency
44    pub fee_currency: Currency,
45    /// Liquidity type (maker/taker)
46    pub liquidity: Liquidity,
47    /// Mark price at time of trade
48    pub mark_price: f64,
49    /// Index price at time of trade
50    pub index_price: f64,
51    /// Instrument kind
52    pub instrument_kind: Option<InstrumentKind>,
53    /// Trade sequence number
54    pub trade_seq: Option<u64>,
55    /// User role in the trade
56    pub user_role: Option<String>,
57    /// Whether this is a block trade
58    pub block_trade: Option<bool>,
59    /// Underlying price (for options)
60    pub underlying_price: Option<f64>,
61    /// Implied volatility (for options)
62    pub iv: Option<f64>,
63    /// Label associated with the order
64    pub label: Option<String>,
65    /// Profit and loss from this trade
66    pub profit_loss: Option<f64>,
67    /// Tick direction
68    pub tick_direction: Option<i32>,
69    /// Whether this trade was self-traded
70    pub self_trade: Option<bool>,
71}
72
73impl Trade {
74    /// Calculate the notional value of the trade
75    pub fn notional_value(&self) -> f64 {
76        self.amount * self.price
77    }
78
79    /// Check if this was a maker trade
80    pub fn is_maker(&self) -> bool {
81        matches!(self.liquidity, Liquidity::Maker | Liquidity::Mixed)
82    }
83
84    /// Check if this was a taker trade
85    pub fn is_taker(&self) -> bool {
86        matches!(self.liquidity, Liquidity::Taker | Liquidity::Mixed)
87    }
88
89    /// Check if this is a buy trade
90    pub fn is_buy(&self) -> bool {
91        self.direction == OrderSide::Buy
92    }
93
94    /// Check if this is a sell trade
95    pub fn is_sell(&self) -> bool {
96        self.direction == OrderSide::Sell
97    }
98
99    /// Get fee as percentage of notional
100    pub fn fee_percentage(&self) -> f64 {
101        if self.notional_value() != 0.0 {
102            (self.fee / self.notional_value()) * 100.0
103        } else {
104            0.0
105        }
106    }
107}
108
109/// Trade statistics
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct TradeStats {
112    /// Total number of trades
113    pub count: u64,
114    /// Total volume
115    pub volume: f64,
116    /// Total fees paid
117    pub total_fees: f64,
118    /// Average price
119    pub avg_price: f64,
120    /// Profit and loss
121    pub pnl: f64,
122    /// Number of winning trades
123    pub winning_trades: u64,
124    /// Number of losing trades
125    pub losing_trades: u64,
126}
127
128impl TradeStats {
129    /// Create empty trade statistics
130    pub fn new() -> Self {
131        Self {
132            count: 0,
133            volume: 0.0,
134            total_fees: 0.0,
135            avg_price: 0.0,
136            pnl: 0.0,
137            winning_trades: 0,
138            losing_trades: 0,
139        }
140    }
141
142    /// Calculate win rate as percentage
143    pub fn win_rate(&self) -> f64 {
144        if self.count > 0 {
145            (self.winning_trades as f64 / self.count as f64) * 100.0
146        } else {
147            0.0
148        }
149    }
150}
151
152impl Default for TradeStats {
153    fn default() -> Self {
154        Self::new()
155    }
156}