gmsol_model/pool/
balance.rs

1use crate::num::{Num, Unsigned};
2use num_traits::CheckedMul;
3
4use super::PoolDelta;
5
6/// Balanced amounts.
7pub trait Balance {
8    /// Unsigned number type.
9    type Num: Num + Unsigned<Signed = Self::Signed>;
10
11    /// Signed number type.
12    type Signed;
13
14    /// Get the long token amount (when this is a token balance), or long usd value (when this is a usd value balance).
15    fn long_amount(&self) -> crate::Result<Self::Num>;
16
17    /// Get the short token amount (when this is a token balance), or short usd value (when this is a usd value balance).
18    fn short_amount(&self) -> crate::Result<Self::Num>;
19}
20
21/// Extension trait for [`Balance`] with utils.
22pub trait BalanceExt: Balance {
23    /// Get the long amount value in USD.
24    fn long_usd_value(&self, price: &Self::Num) -> crate::Result<Self::Num> {
25        self.long_amount()?
26            .checked_mul(price)
27            .ok_or(crate::Error::Overflow)
28    }
29
30    /// Get the short amount value in USD.
31    fn short_usd_value(&self, price: &Self::Num) -> crate::Result<Self::Num> {
32        self.short_amount()?
33            .checked_mul(price)
34            .ok_or(crate::Error::Overflow)
35    }
36
37    /// Get pool value information after applying delta.
38    fn pool_delta_with_amounts(
39        &self,
40        long_token_delta_amount: &Self::Signed,
41        short_token_delta_amount: &Self::Signed,
42        long_token_price: &Self::Num,
43        short_token_price: &Self::Num,
44    ) -> crate::Result<PoolDelta<Self::Num>> {
45        PoolDelta::try_from_delta_amounts(
46            self,
47            long_token_delta_amount,
48            short_token_delta_amount,
49            long_token_price,
50            short_token_price,
51        )
52    }
53
54    /// Get pool value information after applying delta.
55    fn pool_delta_with_values(
56        &self,
57        delta_long_token_usd_value: Self::Signed,
58        delta_short_token_usd_value: Self::Signed,
59        long_token_price: &Self::Num,
60        short_token_price: &Self::Num,
61    ) -> crate::Result<PoolDelta<Self::Num>> {
62        PoolDelta::try_new(
63            self,
64            delta_long_token_usd_value,
65            delta_short_token_usd_value,
66            long_token_price,
67            short_token_price,
68        )
69    }
70
71    /// Merge the amounts with other [`Balance`].
72    ///
73    /// The result [`Balance`] will consider the total amounts (`long_amount + short_amount`) of `self` as the long amount,
74    /// and the total amount of `short` as the short amount.
75    fn merge<B: Balance>(&self, short: B) -> Merged<&Self, B> {
76        Merged(self, short)
77    }
78
79    /// Get amount by side.
80    #[inline]
81    fn amount(&self, is_long: bool) -> crate::Result<Self::Num> {
82        if is_long {
83            self.long_amount()
84        } else {
85            self.short_amount()
86        }
87    }
88}
89
90impl<P: Balance + ?Sized> BalanceExt for P {}
91
92impl<P: Balance> Balance for &P {
93    type Num = P::Num;
94
95    type Signed = P::Signed;
96
97    fn long_amount(&self) -> crate::Result<Self::Num> {
98        (**self).long_amount()
99    }
100
101    fn short_amount(&self) -> crate::Result<Self::Num> {
102        (**self).short_amount()
103    }
104}
105
106/// Merged balanced pool.
107/// A [`Balance`] returned by [`BalanceExt::merge`].
108#[derive(Debug, Clone, Copy)]
109pub struct Merged<A, B>(A, B);
110
111impl<A, B, Num, Signed> Balance for Merged<A, B>
112where
113    Num: crate::num::Num + Unsigned<Signed = Signed>,
114    A: Balance<Num = Num, Signed = Signed>,
115    B: Balance<Num = Num, Signed = Signed>,
116{
117    type Num = Num;
118
119    type Signed = Signed;
120
121    fn long_amount(&self) -> crate::Result<Self::Num> {
122        self.0
123            .long_amount()?
124            .checked_add(&self.0.short_amount()?)
125            .ok_or(crate::Error::Overflow)
126    }
127
128    fn short_amount(&self) -> crate::Result<Self::Num> {
129        self.1
130            .long_amount()?
131            .checked_add(&self.1.short_amount()?)
132            .ok_or(crate::Error::Overflow)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use crate::{test::TestPool, Pool};
140
141    #[test]
142    fn test_merge_balances() -> crate::Result<()> {
143        let mut open_interest_for_long = TestPool::<u64>::default();
144        let mut open_interest_for_short = TestPool::<u64>::default();
145
146        open_interest_for_long.apply_delta_to_long_amount(&1000)?;
147        open_interest_for_long.apply_delta_to_short_amount(&2000)?;
148        open_interest_for_short.apply_delta_to_long_amount(&3000)?;
149        open_interest_for_short.apply_delta_to_short_amount(&4000)?;
150
151        let open_interest = open_interest_for_long.merge(&open_interest_for_short);
152
153        assert_eq!(open_interest.long_amount()?, 3000);
154        assert_eq!(open_interest.short_amount()?, 7000);
155
156        Ok(())
157    }
158}