use std::ops::DerefMut;
use num_traits::{CheckedAdd, Signed};
use crate::{
action::update_funding_state::UpdateFundingState,
num::{Unsigned, UnsignedAbs},
params::{
fee::{FundingFeeParams, LiquidationFeeParams},
FeeParams, PositionParams,
},
price::{Price, Prices},
Balance, BalanceExt, BorrowingFeeMarket, Pool, PoolExt, PositionImpactMarket,
PositionImpactMarketMut, SwapMarket, SwapMarketMut,
};
use super::BaseMarketExt;
pub trait PerpMarket<const DECIMALS: u8>:
SwapMarket<DECIMALS> + PositionImpactMarket<DECIMALS> + BorrowingFeeMarket<DECIMALS>
{
fn funding_factor_per_second(&self) -> &Self::Signed;
fn funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool>;
fn claimable_funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool>;
fn funding_amount_per_size_adjustment(&self) -> Self::Num;
fn funding_fee_params(&self) -> crate::Result<FundingFeeParams<Self::Num>>;
fn position_params(&self) -> crate::Result<PositionParams<Self::Num>>;
fn order_fee_params(&self) -> crate::Result<FeeParams<Self::Num>>;
fn min_collateral_factor_for_open_interest_multiplier(
&self,
is_long: bool,
) -> crate::Result<Self::Num>;
fn liquidation_fee_params(&self) -> crate::Result<LiquidationFeeParams<Self::Num>>;
}
pub trait PerpMarketMut<const DECIMALS: u8>:
SwapMarketMut<DECIMALS> + PositionImpactMarketMut<DECIMALS> + PerpMarket<DECIMALS>
{
fn just_passed_in_seconds_for_funding(&mut self) -> crate::Result<u64>;
fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed;
fn open_interest_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool>;
fn open_interest_in_tokens_pool_mut(&mut self, is_long: bool)
-> crate::Result<&mut Self::Pool>;
fn funding_amount_per_size_pool_mut(&mut self, is_long: bool)
-> crate::Result<&mut Self::Pool>;
fn claimable_funding_amount_per_size_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool>;
fn collateral_sum_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool>;
fn total_borrowing_pool_mut(&mut self) -> crate::Result<&mut Self::Pool>;
fn virtual_inventory_for_positions_pool_mut(
&mut self,
) -> crate::Result<Option<impl DerefMut<Target = Self::Pool>>>;
fn on_insufficient_funding_fee_payment(
&mut self,
_cost_amount: &Self::Num,
_paid_in_collateral_amount: &Self::Num,
_paid_in_secondary_output_amount: &Self::Num,
_is_collateral_token_long: bool,
) -> crate::Result<()> {
Ok(())
}
}
impl<M: PerpMarket<DECIMALS>, const DECIMALS: u8> PerpMarket<DECIMALS> for &mut M {
fn funding_factor_per_second(&self) -> &Self::Signed {
(**self).funding_factor_per_second()
}
fn funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
(**self).funding_amount_per_size_pool(is_long)
}
fn claimable_funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
(**self).claimable_funding_amount_per_size_pool(is_long)
}
fn funding_amount_per_size_adjustment(&self) -> Self::Num {
(**self).funding_amount_per_size_adjustment()
}
fn funding_fee_params(&self) -> crate::Result<FundingFeeParams<Self::Num>> {
(**self).funding_fee_params()
}
fn position_params(&self) -> crate::Result<PositionParams<Self::Num>> {
(**self).position_params()
}
fn order_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
(**self).order_fee_params()
}
fn min_collateral_factor_for_open_interest_multiplier(
&self,
is_long: bool,
) -> crate::Result<Self::Num> {
(**self).min_collateral_factor_for_open_interest_multiplier(is_long)
}
fn liquidation_fee_params(&self) -> crate::Result<LiquidationFeeParams<Self::Num>> {
(**self).liquidation_fee_params()
}
}
impl<M: PerpMarketMut<DECIMALS>, const DECIMALS: u8> PerpMarketMut<DECIMALS> for &mut M {
fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed {
(**self).funding_factor_per_second_mut()
}
fn open_interest_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
(**self).open_interest_pool_mut(is_long)
}
fn open_interest_in_tokens_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool> {
(**self).open_interest_in_tokens_pool_mut(is_long)
}
fn funding_amount_per_size_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool> {
(**self).funding_amount_per_size_pool_mut(is_long)
}
fn claimable_funding_amount_per_size_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool> {
(**self).claimable_funding_amount_per_size_pool_mut(is_long)
}
fn collateral_sum_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
(**self).collateral_sum_pool_mut(is_long)
}
fn total_borrowing_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
(**self).total_borrowing_pool_mut()
}
fn virtual_inventory_for_positions_pool_mut(
&mut self,
) -> crate::Result<Option<impl DerefMut<Target = Self::Pool>>> {
(**self).virtual_inventory_for_positions_pool_mut()
}
fn just_passed_in_seconds_for_funding(&mut self) -> crate::Result<u64> {
(**self).just_passed_in_seconds_for_funding()
}
fn on_insufficient_funding_fee_payment(
&mut self,
cost_amount: &Self::Num,
paid_in_collateral_amount: &Self::Num,
paid_in_secondary_output_amount: &Self::Num,
is_collateral_token_long: bool,
) -> crate::Result<()> {
(**self).on_insufficient_funding_fee_payment(
cost_amount,
paid_in_collateral_amount,
paid_in_secondary_output_amount,
is_collateral_token_long,
)
}
}
pub trait PerpMarketExt<const DECIMALS: u8>: PerpMarket<DECIMALS> {
#[inline]
fn funding_fee_amount_per_size(
&self,
is_long: bool,
is_long_collateral: bool,
) -> crate::Result<Self::Num> {
self.funding_amount_per_size_pool(is_long)?
.amount(is_long_collateral)
}
#[inline]
fn claimable_funding_fee_amount_per_size(
&self,
is_long: bool,
is_long_collateral: bool,
) -> crate::Result<Self::Num> {
self.claimable_funding_amount_per_size_pool(is_long)?
.amount(is_long_collateral)
}
fn validate_open_interest_reserve(
&self,
prices: &Prices<Self::Num>,
is_long: bool,
) -> crate::Result<()> {
let pool_value = self.pool_value_without_pnl_for_one_side(prices, is_long, false)?;
let max_reserved_value =
crate::utils::apply_factor(&pool_value, &self.open_interest_reserve_factor()?)
.ok_or(crate::Error::Computation("calculating max reserved value"))?;
let reserved_value = self.reserved_value(&prices.index_token_price, is_long)?;
if reserved_value > max_reserved_value {
Err(crate::Error::InsufficientReserveForOpenInterest(
reserved_value.to_string(),
max_reserved_value.to_string(),
))
} else {
Ok(())
}
}
fn min_collateral_factor_for_open_interest(
&self,
delta: &Self::Signed,
is_long: bool,
) -> crate::Result<Self::Num> {
let next_open_interest = self
.open_interest()?
.amount(is_long)?
.checked_add_with_signed(delta)
.ok_or(crate::Error::Computation(
"calculating next OI for min collateral factor",
))?;
let factor = self.min_collateral_factor_for_open_interest_multiplier(is_long)?;
crate::utils::apply_factor(&next_open_interest, &factor).ok_or(crate::Error::Computation(
"calculating min collateral factor for OI",
))
}
fn cap_positive_position_price_impact(
&self,
index_token_price: &Price<Self::Num>,
size_delta_usd: &Self::Signed,
impact: &mut Self::Signed,
) -> crate::Result<()> {
use crate::{market::PositionImpactMarketExt, num::UnsignedAbs, utils};
use num_traits::{CheckedMul, Signed};
if !impact.is_negative() {
let impact_pool_amount = self.position_impact_pool_amount()?;
let max_impact = impact_pool_amount
.checked_mul(index_token_price.pick_price(false))
.ok_or(crate::Error::Computation(
"overflow calculating max positive position impact based on pool amount",
))?
.to_signed()?;
if *impact > max_impact {
*impact = max_impact;
}
let params = self.position_params()?;
let max_impact_factor = params.max_positive_position_impact_factor();
let max_impact = utils::apply_factor(&size_delta_usd.unsigned_abs(), max_impact_factor)
.ok_or(crate::Error::Computation(
"calculating max positive position impact based on max factor",
))?
.to_signed()?;
if *impact > max_impact {
*impact = max_impact;
}
}
Ok(())
}
fn cap_negative_position_price_impact(
&self,
size_delta_usd: &Self::Signed,
for_liquidations: bool,
impact: &mut Self::Signed,
) -> crate::Result<Self::Num> {
use crate::{num::UnsignedAbs, utils};
use num_traits::{CheckedSub, Signed, Zero};
let mut impact_diff = Zero::zero();
if impact.is_negative() {
let params = self.position_params()?;
let max_impact_factor = if for_liquidations {
params.max_position_impact_factor_for_liquidations()
} else {
params.max_negative_position_impact_factor()
};
let min_impact = utils::apply_factor(&size_delta_usd.unsigned_abs(), max_impact_factor)
.ok_or(crate::Error::Computation(
"calculating max negative position impact based on max factor",
))?
.to_opposite_signed()?;
if *impact < min_impact {
impact_diff = min_impact
.checked_sub(impact)
.ok_or(crate::Error::Computation(
"overflow calculating impact diff",
))?
.unsigned_abs();
*impact = min_impact;
}
}
Ok(impact_diff)
}
}
impl<M: PerpMarket<DECIMALS>, const DECIMALS: u8> PerpMarketExt<DECIMALS> for M {}
pub trait PerpMarketMutExt<const DECIMALS: u8>: PerpMarketMut<DECIMALS> {
fn update_funding(
&mut self,
prices: &Prices<Self::Num>,
) -> crate::Result<UpdateFundingState<&mut Self, DECIMALS>>
where
Self: Sized,
{
UpdateFundingState::try_new(self, prices)
}
fn apply_delta_to_funding_amount_per_size(
&mut self,
is_long: bool,
is_long_collateral: bool,
delta: &Self::Signed,
) -> crate::Result<()> {
self.funding_amount_per_size_pool_mut(is_long)?
.apply_delta_amount(is_long_collateral, delta)
}
fn apply_delta_to_claimable_funding_amount_per_size(
&mut self,
is_long: bool,
is_long_collateral: bool,
delta: &Self::Signed,
) -> crate::Result<()> {
self.claimable_funding_amount_per_size_pool_mut(is_long)?
.apply_delta_amount(is_long_collateral, delta)
}
fn apply_delta_to_open_interest(
&mut self,
is_long: bool,
is_long_collateral: bool,
delta: &Self::Signed,
) -> crate::Result<()> {
let max_open_interest = self.max_open_interest(is_long)?;
let open_interest = self.open_interest_pool_mut(is_long)?;
if is_long_collateral {
open_interest.apply_delta_to_long_amount(delta)?;
} else {
open_interest.apply_delta_to_short_amount(delta)?;
}
if delta.is_positive() {
let is_exceeded = open_interest
.long_amount()?
.checked_add(&open_interest.short_amount()?)
.map(|total| total > max_open_interest)
.unwrap_or(true);
if is_exceeded {
return Err(crate::Error::MaxOpenInterestExceeded);
}
}
if let Some(mut pool) = self.virtual_inventory_for_positions_pool_mut()? {
let is_increased = !delta.is_negative();
let abs_delta = delta.unsigned_abs().to_signed()?;
match (is_long, is_increased) {
(true, true) | (false, false) => {
pool.apply_delta_to_long_amount(&abs_delta)?;
}
(true, false) | (false, true) => {
pool.apply_delta_to_short_amount(&abs_delta)?;
}
}
*pool = pool.checked_cancel_amounts()?;
}
Ok(())
}
}
impl<M: PerpMarketMut<DECIMALS>, const DECIMALS: u8> PerpMarketMutExt<DECIMALS> for M {}