gmsol_model/market/
position_impact.rs

1use num_traits::{CheckedSub, FromPrimitive, Zero};
2
3use crate::{
4    action::distribute_position_impact::DistributePositionImpact,
5    params::{position::PositionImpactDistributionParams, PriceImpactParams},
6    Balance, BaseMarket, BaseMarketMut, Pool,
7};
8
9/// A market with position impact pool.
10pub trait PositionImpactMarket<const DECIMALS: u8>: BaseMarket<DECIMALS> {
11    /// Get position impact pool.
12    fn position_impact_pool(&self) -> crate::Result<&Self::Pool>;
13
14    /// Get the position impact params.
15    fn position_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>>;
16
17    /// Get position impact distribution params.
18    fn position_impact_distribution_params(
19        &self,
20    ) -> crate::Result<PositionImpactDistributionParams<Self::Num>>;
21
22    /// Get the passed time in seconds for the given kind of clock.
23    fn passed_in_seconds_for_position_impact_distribution(&self) -> crate::Result<u64>;
24}
25
26/// A mutable market with position impact pool.
27pub trait PositionImpactMarketMut<const DECIMALS: u8>:
28    BaseMarketMut<DECIMALS> + PositionImpactMarket<DECIMALS>
29{
30    /// Get position impact pool mutably.
31    /// # Requirements
32    /// - This method must return `Ok` if [`PositionImpactMarket::position_impact_pool`] does.
33    fn position_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool>;
34
35    /// Get the just passed time in seconds for the given kind of clock.
36    fn just_passed_in_seconds_for_position_impact_distribution(&mut self) -> crate::Result<u64>;
37}
38
39impl<M: PositionImpactMarket<DECIMALS>, const DECIMALS: u8> PositionImpactMarket<DECIMALS>
40    for &mut M
41{
42    fn position_impact_pool(&self) -> crate::Result<&Self::Pool> {
43        (**self).position_impact_pool()
44    }
45
46    fn position_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
47        (**self).position_impact_params()
48    }
49
50    fn position_impact_distribution_params(
51        &self,
52    ) -> crate::Result<PositionImpactDistributionParams<Self::Num>> {
53        (**self).position_impact_distribution_params()
54    }
55
56    fn passed_in_seconds_for_position_impact_distribution(&self) -> crate::Result<u64> {
57        (**self).passed_in_seconds_for_position_impact_distribution()
58    }
59}
60
61impl<M: PositionImpactMarketMut<DECIMALS>, const DECIMALS: u8> PositionImpactMarketMut<DECIMALS>
62    for &mut M
63{
64    fn position_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
65        (**self).position_impact_pool_mut()
66    }
67
68    fn just_passed_in_seconds_for_position_impact_distribution(&mut self) -> crate::Result<u64> {
69        (**self).just_passed_in_seconds_for_position_impact_distribution()
70    }
71}
72
73/// Extension trait of [`PositionImpactMarket`].
74pub trait PositionImpactMarketExt<const DECIMALS: u8>: PositionImpactMarket<DECIMALS> {
75    /// Get position impact pool amount.
76    #[inline]
77    fn position_impact_pool_amount(&self) -> crate::Result<Self::Num> {
78        self.position_impact_pool()?.long_amount()
79    }
80
81    /// Get pending position impact pool distribution amount.
82    fn pending_position_impact_pool_distribution_amount(
83        &self,
84        duration_in_secs: u64,
85    ) -> crate::Result<(Self::Num, Self::Num)> {
86        use crate::utils;
87
88        let current_amount = self.position_impact_pool_amount()?;
89        let params = self.position_impact_distribution_params()?;
90        let min_position_impact_pool_amount = params.min_position_impact_pool_amount();
91        if params.distribute_factor().is_zero()
92            || current_amount <= *min_position_impact_pool_amount
93        {
94            return Ok((Zero::zero(), current_amount));
95        }
96        let max_distribution_amount = current_amount
97            .checked_sub(min_position_impact_pool_amount)
98            .ok_or(crate::Error::Computation(
99                "calculating max distribution amount",
100            ))?;
101
102        let duration_value = Self::Num::from_u64(duration_in_secs).ok_or(crate::Error::Convert)?;
103        let mut distribution_amount =
104            utils::apply_factor(&duration_value, params.distribute_factor())
105                .ok_or(crate::Error::Computation("calculating distribution amount"))?;
106        if distribution_amount > max_distribution_amount {
107            distribution_amount = max_distribution_amount;
108        }
109        let next_amount =
110            current_amount
111                .checked_sub(&distribution_amount)
112                .ok_or(crate::Error::Computation(
113                    "calculating next position impact amount",
114                ))?;
115        Ok((distribution_amount, next_amount))
116    }
117}
118
119impl<M: PositionImpactMarket<DECIMALS> + ?Sized, const DECIMALS: u8>
120    PositionImpactMarketExt<DECIMALS> for M
121{
122}
123
124/// Extension trait of [`PositionImpactMarketMut`].
125pub trait PositionImpactMarketMutExt<const DECIMALS: u8>:
126    PositionImpactMarketMut<DECIMALS>
127{
128    /// Apply delta to the position impact pool.
129    fn apply_delta_to_position_impact_pool(&mut self, delta: &Self::Signed) -> crate::Result<()> {
130        self.position_impact_pool_mut()?
131            .apply_delta_to_long_amount(delta)
132    }
133
134    /// Create a [`DistributePositionImpact`] action.
135    fn distribute_position_impact(
136        &mut self,
137    ) -> crate::Result<DistributePositionImpact<&mut Self, DECIMALS>>
138    where
139        Self: Sized,
140    {
141        Ok(DistributePositionImpact::from(self))
142    }
143}
144
145impl<M: PositionImpactMarketMut<DECIMALS> + ?Sized, const DECIMALS: u8>
146    PositionImpactMarketMutExt<DECIMALS> for M
147{
148}