use num_traits::{CheckedSub, FromPrimitive, Zero};
use crate::{
action::distribute_position_impact::DistributePositionImpact,
params::{position::PositionImpactDistributionParams, PriceImpactParams},
Balance, BaseMarket, BaseMarketMut, Pool,
};
pub trait PositionImpactMarket<const DECIMALS: u8>: BaseMarket<DECIMALS> {
fn position_impact_pool(&self) -> crate::Result<&Self::Pool>;
fn position_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>>;
fn position_impact_distribution_params(
&self,
) -> crate::Result<PositionImpactDistributionParams<Self::Num>>;
fn passed_in_seconds_for_position_impact_distribution(&self) -> crate::Result<u64>;
}
pub trait PositionImpactMarketMut<const DECIMALS: u8>:
BaseMarketMut<DECIMALS> + PositionImpactMarket<DECIMALS>
{
fn position_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool>;
fn just_passed_in_seconds_for_position_impact_distribution(&mut self) -> crate::Result<u64>;
}
impl<M: PositionImpactMarket<DECIMALS>, const DECIMALS: u8> PositionImpactMarket<DECIMALS>
for &mut M
{
fn position_impact_pool(&self) -> crate::Result<&Self::Pool> {
(**self).position_impact_pool()
}
fn position_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
(**self).position_impact_params()
}
fn position_impact_distribution_params(
&self,
) -> crate::Result<PositionImpactDistributionParams<Self::Num>> {
(**self).position_impact_distribution_params()
}
fn passed_in_seconds_for_position_impact_distribution(&self) -> crate::Result<u64> {
(**self).passed_in_seconds_for_position_impact_distribution()
}
}
impl<M: PositionImpactMarketMut<DECIMALS>, const DECIMALS: u8> PositionImpactMarketMut<DECIMALS>
for &mut M
{
fn position_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
(**self).position_impact_pool_mut()
}
fn just_passed_in_seconds_for_position_impact_distribution(&mut self) -> crate::Result<u64> {
(**self).just_passed_in_seconds_for_position_impact_distribution()
}
}
pub trait PositionImpactMarketExt<const DECIMALS: u8>: PositionImpactMarket<DECIMALS> {
#[inline]
fn position_impact_pool_amount(&self) -> crate::Result<Self::Num> {
self.position_impact_pool()?.long_amount()
}
fn pending_position_impact_pool_distribution_amount(
&self,
duration_in_secs: u64,
) -> crate::Result<(Self::Num, Self::Num)> {
use crate::utils;
let current_amount = self.position_impact_pool_amount()?;
let params = self.position_impact_distribution_params()?;
let min_position_impact_pool_amount = params.min_position_impact_pool_amount();
if params.distribute_factor().is_zero()
|| current_amount <= *min_position_impact_pool_amount
{
return Ok((Zero::zero(), current_amount));
}
let max_distribution_amount = current_amount
.checked_sub(min_position_impact_pool_amount)
.ok_or(crate::Error::Computation(
"calculating max distribution amount",
))?;
let duration_value = Self::Num::from_u64(duration_in_secs).ok_or(crate::Error::Convert)?;
let mut distribution_amount =
utils::apply_factor(&duration_value, params.distribute_factor())
.ok_or(crate::Error::Computation("calculating distribution amount"))?;
if distribution_amount > max_distribution_amount {
distribution_amount = max_distribution_amount;
}
let next_amount =
current_amount
.checked_sub(&distribution_amount)
.ok_or(crate::Error::Computation(
"calculating next position impact amount",
))?;
Ok((distribution_amount, next_amount))
}
}
impl<M: PositionImpactMarket<DECIMALS> + ?Sized, const DECIMALS: u8>
PositionImpactMarketExt<DECIMALS> for M
{
}
pub trait PositionImpactMarketMutExt<const DECIMALS: u8>:
PositionImpactMarketMut<DECIMALS>
{
fn apply_delta_to_position_impact_pool(&mut self, delta: &Self::Signed) -> crate::Result<()> {
self.position_impact_pool_mut()?
.apply_delta_to_long_amount(delta)
}
fn distribute_position_impact(
&mut self,
) -> crate::Result<DistributePositionImpact<&mut Self, DECIMALS>>
where
Self: Sized,
{
Ok(DistributePositionImpact::from(self))
}
}
impl<M: PositionImpactMarketMut<DECIMALS> + ?Sized, const DECIMALS: u8>
PositionImpactMarketMutExt<DECIMALS> for M
{
}