use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Direction {
Buy,
Sell,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Position {
pub instrument_name: String,
pub size: f64,
pub direction: Direction,
pub average_price: f64,
pub average_price_usd: Option<f64>,
pub delta: Option<f64>,
pub estimated_liquidation_price: Option<f64>,
pub floating_profit_loss: Option<f64>,
pub floating_profit_loss_usd: Option<f64>,
pub gamma: Option<f64>,
pub index_price: Option<f64>,
pub initial_margin: Option<f64>,
pub interest_value: Option<f64>,
pub kind: Option<String>,
pub leverage: Option<i32>,
pub maintenance_margin: Option<f64>,
pub mark_price: Option<f64>,
pub open_orders_margin: Option<f64>,
pub realized_funding: Option<f64>,
pub realized_profit_loss: Option<f64>,
pub settlement_price: Option<f64>,
pub size_currency: Option<f64>,
pub theta: Option<f64>,
pub total_profit_loss: Option<f64>,
pub vega: Option<f64>,
pub unrealized_profit_loss: Option<f64>,
}
impl_json_display!(Position);
impl_json_debug_pretty!(Position);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_direction_serialization() {
let buy = Direction::Buy;
let sell = Direction::Sell;
let buy_json = serde_json::to_string(&buy).unwrap();
let sell_json = serde_json::to_string(&sell).unwrap();
assert_eq!(buy_json, "\"buy\"");
assert_eq!(sell_json, "\"sell\"");
let buy_deserialized: Direction = serde_json::from_str(&buy_json).unwrap();
let sell_deserialized: Direction = serde_json::from_str(&sell_json).unwrap();
assert_eq!(buy_deserialized, Direction::Buy);
assert_eq!(sell_deserialized, Direction::Sell);
}
#[test]
fn test_position_creation() {
let position = Position {
instrument_name: "BTC-PERPETUAL".to_string(),
size: 1.5,
direction: Direction::Buy,
average_price: 50000.0,
average_price_usd: None,
delta: Some(0.5),
estimated_liquidation_price: None,
floating_profit_loss: Some(100.0),
floating_profit_loss_usd: None,
gamma: Some(0.001),
index_price: Some(50100.0),
initial_margin: Some(500.0),
interest_value: None,
kind: Some("future".to_string()),
leverage: Some(10),
maintenance_margin: Some(250.0),
mark_price: Some(50050.0),
open_orders_margin: None,
realized_funding: None,
realized_profit_loss: Some(50.0),
settlement_price: Some(50000.0),
size_currency: None,
theta: None,
total_profit_loss: Some(150.0),
vega: None,
unrealized_profit_loss: Some(100.0),
};
assert_eq!(position.instrument_name, "BTC-PERPETUAL");
assert_eq!(position.size, 1.5);
assert!(matches!(position.direction, Direction::Buy));
assert_eq!(position.average_price, 50000.0);
}
#[test]
fn test_position_serialization_roundtrip() {
let position = Position {
instrument_name: "ETH-PERPETUAL".to_string(),
size: -2.0,
direction: Direction::Sell,
average_price: 3500.0,
average_price_usd: Some(3500.0),
delta: None,
estimated_liquidation_price: Some(4000.0),
floating_profit_loss: Some(-50.0),
floating_profit_loss_usd: Some(-50.0),
gamma: None,
index_price: Some(3550.0),
initial_margin: Some(350.0),
interest_value: None,
kind: Some("future".to_string()),
leverage: Some(5),
maintenance_margin: Some(175.0),
mark_price: Some(3525.0),
open_orders_margin: None,
realized_funding: None,
realized_profit_loss: None,
settlement_price: None,
size_currency: Some(-2.0),
theta: None,
total_profit_loss: Some(-50.0),
vega: None,
unrealized_profit_loss: Some(-50.0),
};
let json = serde_json::to_string(&position).unwrap();
let deserialized: Position = serde_json::from_str(&json).unwrap();
assert_eq!(position.instrument_name, deserialized.instrument_name);
assert_eq!(position.size, deserialized.size);
assert_eq!(position.direction, deserialized.direction);
assert_eq!(position.average_price, deserialized.average_price);
}
}