1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#[derive(Debug, Clone, Copy)]
/// Describes the margin information of the account
pub struct Margin {
    /// The wallet balance of account denoted in BASE currency
    wallet_balance: f64,
    /// The position margin of account denoted in BASE currency
    position_margin: f64,
    /// The order margin of account denoted in BASE currency
    order_margin: f64,
    /// The available balance of account denoted in BASE currency
    available_balance: f64,
}

impl Margin {
    /// Create a new margin account with an initial balance denoted in BASE currency
    pub fn new_init(init_balance: f64) -> Self {
        Margin {
            wallet_balance: init_balance,
            position_margin: 0.0,
            order_margin: 0.0,
            available_balance: init_balance,
        }
    }

    /// Create a new Margin with all fields custom
    pub fn new(
        wallet_balance: f64,
        position_margin: f64,
        order_margin: f64,
        available_balance: f64,
    ) -> Self {
        Margin {
            wallet_balance,
            position_margin,
            order_margin,
            available_balance,
        }
    }

    /// Set a new order margin
    pub(crate) fn set_order_margin(&mut self, order_margin: f64) {
        debug_assert!(order_margin >= 0.0);

        self.order_margin = order_margin;
        self.available_balance = self.wallet_balance - self.position_margin - self.order_margin;

        debug_assert!(self.available_balance >= 0.0);
    }

    /// Change the position margin by a given delta and adjust available balance accordingly
    pub(crate) fn set_position_margin(&mut self, delta: f64) {
        self.position_margin = delta;
        self.available_balance = self.wallet_balance - self.order_margin - self.position_margin;

        debug_assert!(self.position_margin >= 0.0);
        debug_assert!(self.position_margin <= self.wallet_balance);
        debug_assert!(self.available_balance >= 0.0);
        debug_assert!(self.available_balance <= self.wallet_balance);
    }

    /// Change the balance by a given delta e.g. from realizing profit or loss
    /// Return true if successful
    pub(crate) fn change_balance(&mut self, delta: f64) {
        self.wallet_balance += delta;
        self.available_balance += delta;

        // debug_assert!(self.wallet_balance >= 0.0);
        // debug_assert!(self.available_balance >= 0.0);
    }

    /// Return the wallet balance of account
    pub fn wallet_balance(&self) -> f64 {
        self.wallet_balance
    }

    /// Return the position margin of account
    pub fn position_margin(&self) -> f64 {
        self.position_margin
    }

    /// Return the used order margin of account
    pub fn order_margin(&self) -> f64 {
        self.order_margin
    }

    /// Return the available balance of account
    pub fn available_balance(&self) -> f64 {
        self.available_balance
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn margin_set_order_margin() {
        let mut margin = Margin::new_init(1.0);
        margin.set_order_margin(1.0);
        assert_eq!(margin.wallet_balance, 1.0);
        assert_eq!(margin.position_margin, 0.0);
        assert_eq!(margin.order_margin, 1.0);
        assert_eq!(margin.available_balance, 0.0);
    }

    #[test]
    fn margin_change_balance() {
        let mut margin = Margin::new_init(1.0);

        margin.change_balance(0.05);
        assert_eq!(margin.wallet_balance, 1.05);
        assert_eq!(margin.position_margin, 0.0);
        assert_eq!(margin.order_margin, 0.0);
        assert_eq!(margin.available_balance, 1.05);
    }
}