use chrono::{DateTime, Utc};
use derive_more::Constructor;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize, Constructor,
)]
pub struct Position {
pub quantity: Decimal,
pub entry_price: Option<Decimal>,
pub unrealized_pnl: Option<Decimal>,
pub margin_used: Option<Decimal>,
pub liquidation_price: Option<Decimal>,
pub leverage: Option<Decimal>,
pub time_exchange: DateTime<Utc>,
}
impl Position {
pub fn is_flat(&self) -> bool {
self.quantity.is_zero()
}
pub fn is_long(&self) -> bool {
self.quantity.is_sign_positive() && !self.quantity.is_zero()
}
pub fn is_short(&self) -> bool {
self.quantity.is_sign_negative()
}
pub fn abs_quantity(&self) -> Decimal {
self.quantity.abs()
}
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
#[test]
fn test_position_side_detection() {
let now = Utc::now();
let long = Position::new(dec!(1.5), None, None, None, None, None, now);
assert!(long.is_long());
assert!(!long.is_short());
assert!(!long.is_flat());
let short = Position::new(dec!(-1.5), None, None, None, None, None, now);
assert!(!short.is_long());
assert!(short.is_short());
assert!(!short.is_flat());
let flat = Position::new(dec!(0), None, None, None, None, None, now);
assert!(!flat.is_long());
assert!(!flat.is_short());
assert!(flat.is_flat());
}
#[test]
fn test_abs_quantity() {
let now = Utc::now();
let short = Position::new(dec!(-2.5), None, None, None, None, None, now);
assert_eq!(short.abs_quantity(), dec!(2.5));
}
}