1use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "lowercase")]
19pub enum Direction {
20 Buy,
22 Sell,
24}
25
26#[derive(Clone, Serialize, Deserialize)]
31pub struct Position {
32 pub instrument_name: String,
34 pub size: f64,
36 pub direction: Direction,
38 pub average_price: f64,
40 pub average_price_usd: Option<f64>,
42 pub delta: Option<f64>,
44 pub estimated_liquidation_price: Option<f64>,
46 pub floating_profit_loss: Option<f64>,
48 pub floating_profit_loss_usd: Option<f64>,
50 pub gamma: Option<f64>,
52 pub index_price: Option<f64>,
54 pub initial_margin: Option<f64>,
56 pub interest_value: Option<f64>,
58 pub kind: Option<String>,
60 pub leverage: Option<i32>,
62 pub maintenance_margin: Option<f64>,
64 pub mark_price: Option<f64>,
66 pub open_orders_margin: Option<f64>,
68 pub realized_funding: Option<f64>,
70 pub realized_profit_loss: Option<f64>,
72 pub settlement_price: Option<f64>,
74 pub size_currency: Option<f64>,
76 pub theta: Option<f64>,
78 pub total_profit_loss: Option<f64>,
80 pub vega: Option<f64>,
82 pub unrealized_profit_loss: Option<f64>,
84}
85
86impl_json_display!(Position);
87impl_json_debug_pretty!(Position);
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_direction_serialization() {
95 let buy = Direction::Buy;
96 let sell = Direction::Sell;
97
98 let buy_json = serde_json::to_string(&buy).unwrap();
99 let sell_json = serde_json::to_string(&sell).unwrap();
100
101 assert_eq!(buy_json, "\"buy\"");
102 assert_eq!(sell_json, "\"sell\"");
103
104 let buy_deserialized: Direction = serde_json::from_str(&buy_json).unwrap();
105 let sell_deserialized: Direction = serde_json::from_str(&sell_json).unwrap();
106
107 assert_eq!(buy_deserialized, Direction::Buy);
108 assert_eq!(sell_deserialized, Direction::Sell);
109 }
110
111 #[test]
112 fn test_position_creation() {
113 let position = Position {
114 instrument_name: "BTC-PERPETUAL".to_string(),
115 size: 1.5,
116 direction: Direction::Buy,
117 average_price: 50000.0,
118 average_price_usd: None,
119 delta: Some(0.5),
120 estimated_liquidation_price: None,
121 floating_profit_loss: Some(100.0),
122 floating_profit_loss_usd: None,
123 gamma: Some(0.001),
124 index_price: Some(50100.0),
125 initial_margin: Some(500.0),
126 interest_value: None,
127 kind: Some("future".to_string()),
128 leverage: Some(10),
129 maintenance_margin: Some(250.0),
130 mark_price: Some(50050.0),
131 open_orders_margin: None,
132 realized_funding: None,
133 realized_profit_loss: Some(50.0),
134 settlement_price: Some(50000.0),
135 size_currency: None,
136 theta: None,
137 total_profit_loss: Some(150.0),
138 vega: None,
139 unrealized_profit_loss: Some(100.0),
140 };
141
142 assert_eq!(position.instrument_name, "BTC-PERPETUAL");
143 assert_eq!(position.size, 1.5);
144 assert!(matches!(position.direction, Direction::Buy));
145 assert_eq!(position.average_price, 50000.0);
146 }
147
148 #[test]
149 fn test_position_serialization_roundtrip() {
150 let position = Position {
151 instrument_name: "ETH-PERPETUAL".to_string(),
152 size: -2.0,
153 direction: Direction::Sell,
154 average_price: 3500.0,
155 average_price_usd: Some(3500.0),
156 delta: None,
157 estimated_liquidation_price: Some(4000.0),
158 floating_profit_loss: Some(-50.0),
159 floating_profit_loss_usd: Some(-50.0),
160 gamma: None,
161 index_price: Some(3550.0),
162 initial_margin: Some(350.0),
163 interest_value: None,
164 kind: Some("future".to_string()),
165 leverage: Some(5),
166 maintenance_margin: Some(175.0),
167 mark_price: Some(3525.0),
168 open_orders_margin: None,
169 realized_funding: None,
170 realized_profit_loss: None,
171 settlement_price: None,
172 size_currency: Some(-2.0),
173 theta: None,
174 total_profit_loss: Some(-50.0),
175 vega: None,
176 unrealized_profit_loss: Some(-50.0),
177 };
178
179 let json = serde_json::to_string(&position).unwrap();
180 let deserialized: Position = serde_json::from_str(&json).unwrap();
181
182 assert_eq!(position.instrument_name, deserialized.instrument_name);
183 assert_eq!(position.size, deserialized.size);
184 assert_eq!(position.direction, deserialized.direction);
185 assert_eq!(position.average_price, deserialized.average_price);
186 }
187}