#![allow(clippy::arithmetic_side_effects)]
use std::{
collections::HashMap,
fmt,
ops::{Deref, DerefMut},
time::{Duration, Instant},
};
use crate::{
action::decrease_position::DecreasePositionSwapType,
clock::ClockKind,
fixed::FixedPointOps,
market::{
BaseMarket, BorrowingFeeMarketMut, LiquidityMarket, LiquidityMarketMut, PerpMarket,
PnlFactorKind, PositionImpactMarket, SwapMarket,
},
num::{MulDiv, Num, Unsigned, UnsignedAbs},
params::{
fee::{
BorrowingFeeKinkModelParams, BorrowingFeeKinkModelParamsForOneSide, BorrowingFeeParams,
FundingFeeParams, LiquidationFeeParams,
},
position::PositionImpactDistributionParams,
FeeParams, PositionParams, PriceImpactParams,
},
pool::{Balance, Delta, Pool},
position::Position,
BaseMarketMut, BorrowingFeeMarket, PerpMarketMut, PositionImpactMarketMut, PositionMut,
PositionState, PositionStateMut, SwapMarketMut,
};
use num_traits::{CheckedSub, Signed};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct TestPool<T> {
long_amount: T,
short_amount: T,
}
impl<T> Balance for TestPool<T>
where
T: MulDiv + Num + CheckedSub,
{
type Num = T;
type Signed = T::Signed;
fn long_amount(&self) -> crate::Result<Self::Num> {
Ok(self.long_amount.clone())
}
fn short_amount(&self) -> crate::Result<Self::Num> {
Ok(self.short_amount.clone())
}
}
impl<T> Pool for TestPool<T>
where
T: MulDiv + Num + CheckedSub,
{
fn checked_apply_delta(&self, delta: Delta<&Self::Signed>) -> crate::Result<Self> {
let mut ans = self.clone();
if let Some(amount) = delta.long() {
ans.apply_delta_to_long_amount(amount)?;
}
if let Some(amount) = delta.short() {
ans.apply_delta_to_short_amount(amount)?;
}
Ok(ans)
}
fn apply_delta_to_long_amount(&mut self, delta: &Self::Signed) -> Result<(), crate::Error> {
if delta.is_positive() {
self.long_amount = self
.long_amount
.checked_add(&delta.unsigned_abs())
.ok_or(crate::Error::Overflow)?;
} else {
self.long_amount = self
.long_amount
.checked_sub(&delta.unsigned_abs())
.ok_or(crate::Error::Computation("decreasing long amount"))?;
}
Ok(())
}
fn apply_delta_to_short_amount(&mut self, delta: &Self::Signed) -> Result<(), crate::Error> {
if delta.is_positive() {
self.short_amount = self
.short_amount
.checked_add(&delta.unsigned_abs())
.ok_or(crate::Error::Overflow)?;
} else {
self.short_amount = self
.short_amount
.checked_sub(&delta.unsigned_abs())
.ok_or(crate::Error::Computation("decreasing short amount"))?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct MaxPnlFactors<T> {
pub deposit: T,
pub withdrawal: T,
pub trader: T,
pub adl: T,
}
#[derive(Debug, Clone, Copy, Default)]
struct Clock {
advanced: Duration,
}
impl Clock {
fn now(&self) -> Instant {
Instant::now() + self.advanced
}
fn move_forward(&mut self, duration: Duration) {
self.advanced += duration;
}
}
#[derive(Debug, Clone)]
pub struct TestMarket<T: Unsigned, const DECIMALS: u8> {
config: TestMarketConfig<T, DECIMALS>,
total_supply: T,
value_to_amount_divisor: T,
funding_amount_per_size_adjustment: T,
primary: TestPool<T>,
swap_impact: TestPool<T>,
fee: TestPool<T>,
open_interest: (TestPool<T>, TestPool<T>),
open_interest_in_tokens: (TestPool<T>, TestPool<T>),
position_impact: TestPool<T>,
borrowing_factor: TestPool<T>,
funding_factor_per_second: T::Signed,
funding_amount_per_size: (TestPool<T>, TestPool<T>),
claimable_funding_amount_per_size: (TestPool<T>, TestPool<T>),
collateral_sum: (TestPool<T>, TestPool<T>),
total_borrowing: TestPool<T>,
clock: Clock,
clocks: HashMap<ClockKind, Instant>,
}
impl<T: Unsigned, const DECIMALS: u8> TestMarket<T, DECIMALS> {
fn new(
value_to_amount_divisor: T,
funding_amount_per_size_adjustment: T,
config: TestMarketConfig<T, DECIMALS>,
) -> Self
where
T: Default,
T::Signed: Default,
{
Self {
config,
total_supply: Default::default(),
value_to_amount_divisor,
funding_amount_per_size_adjustment,
primary: Default::default(),
swap_impact: Default::default(),
fee: Default::default(),
open_interest: Default::default(),
open_interest_in_tokens: Default::default(),
position_impact: Default::default(),
borrowing_factor: Default::default(),
funding_factor_per_second: Default::default(),
funding_amount_per_size: Default::default(),
claimable_funding_amount_per_size: Default::default(),
collateral_sum: Default::default(),
total_borrowing: Default::default(),
clocks: Default::default(),
clock: Default::default(),
}
}
pub fn move_clock_forward(&mut self, duration: Duration) {
self.clock.move_forward(duration);
}
}
impl TestMarket<u64, 9> {
pub fn with_config(config: TestMarketConfig<u64, 9>) -> Self {
Self::new(1, 10_000, config)
}
}
#[cfg(feature = "u128")]
impl TestMarket<u128, 20> {
pub fn with_config(config: TestMarketConfig<u128, 20>) -> Self {
Self::new(10u128.pow(20 - 9), 10u128.pow(10), config)
}
}
impl Default for TestMarket<u64, 9> {
fn default() -> Self {
Self::with_config(Default::default())
}
}
#[cfg(feature = "u128")]
impl Default for TestMarket<u128, 20> {
fn default() -> Self {
Self::with_config(Default::default())
}
}
const SECONDS_PER_YEAR: u64 = 365 * 24 * 3600;
#[derive(Debug, Clone)]
pub struct TestMarketConfig<T, const DECIMALS: u8> {
pub swap_impact_params: PriceImpactParams<T>,
pub swap_fee_params: FeeParams<T>,
pub position_params: PositionParams<T>,
pub position_impact_params: PriceImpactParams<T>,
pub order_fee_params: FeeParams<T>,
pub position_impact_distribution_params: PositionImpactDistributionParams<T>,
pub borrowing_fee_params: BorrowingFeeParams<T>,
pub borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide<T>,
pub funding_fee_params: FundingFeeParams<T>,
pub reserve_factor: T,
pub open_interest_reserve_factor: T,
pub max_pnl_factors: MaxPnlFactors<T>,
pub min_pnl_factor_after_adl: T,
pub max_pool_amount: T,
pub max_pool_value_for_deposit: T,
pub max_open_interest: T,
pub min_collateral_factor_for_oi: T,
pub ignore_open_interest_for_usage_factor: bool,
pub liquidation_fee_params: LiquidationFeeParams<T>,
}
impl Default for TestMarketConfig<u64, 9> {
fn default() -> Self {
Self {
swap_impact_params: PriceImpactParams::builder()
.exponent(2_000_000_000)
.positive_factor(4)
.negative_factor(8)
.build(),
swap_fee_params: FeeParams::builder()
.fee_receiver_factor(370_000_000)
.positive_impact_fee_factor(500_000)
.negative_impact_fee_factor(700_000)
.build(),
position_params: PositionParams::new(
1_000_000_000,
1_000_000_000,
10_000_000,
5_000_000,
5_000_000,
2_500_000,
),
position_impact_params: PriceImpactParams::builder()
.exponent(2_000_000_000)
.positive_factor(1)
.negative_factor(2)
.build(),
order_fee_params: FeeParams::builder()
.fee_receiver_factor(370_000_000)
.positive_impact_fee_factor(500_000)
.negative_impact_fee_factor(700_000)
.build(),
position_impact_distribution_params: PositionImpactDistributionParams::builder()
.distribute_factor(1_000_000_000)
.min_position_impact_pool_amount(1_000_000_000)
.build(),
borrowing_fee_params: BorrowingFeeParams::builder()
.receiver_factor(370_000_000)
.factor_for_long(28)
.factor_for_short(28)
.exponent_for_long(1_000_000_000)
.exponent_for_short(1_000_000_000)
.build(),
borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide::builder()
.optimal_usage_factor(750_000_000)
.base_borrowing_factor(600_000_000 / SECONDS_PER_YEAR)
.above_optimal_usage_borrowing_factor(1_500_000_000 / SECONDS_PER_YEAR)
.build(),
funding_fee_params: FundingFeeParams::builder()
.exponent(1_000_000_000)
.funding_factor(20)
.max_factor_per_second(10)
.min_factor_per_second(1)
.increase_factor_per_second(10)
.decrease_factor_per_second(0)
.threshold_for_stable_funding(50_000_000)
.threshold_for_decrease_funding(0)
.build(),
reserve_factor: 1_000_000_000,
max_pnl_factors: MaxPnlFactors {
deposit: 600_000_000,
withdrawal: 300_000_000,
trader: 500_000_000,
adl: 500_000_000,
},
min_pnl_factor_after_adl: 0,
open_interest_reserve_factor: 1_000_000_000,
max_pool_amount: 1_000_000_000 * 1_000_000_000,
max_pool_value_for_deposit: u64::MAX,
max_open_interest: u64::MAX,
min_collateral_factor_for_oi: 5 * 10u64.pow(6) / 83_000_000,
ignore_open_interest_for_usage_factor: false,
liquidation_fee_params: LiquidationFeeParams::builder()
.factor(2_000_000)
.receiver_factor(370_000_000)
.build(),
}
}
}
#[cfg(feature = "u128")]
impl Default for TestMarketConfig<u128, 20> {
fn default() -> Self {
Self {
swap_impact_params: PriceImpactParams::builder()
.exponent(200_000_000_000_000_000_000)
.positive_factor(400_000_000_000)
.negative_factor(800_000_000_000)
.build(),
swap_fee_params: FeeParams::builder()
.fee_receiver_factor(37_000_000_000_000_000_000)
.positive_impact_fee_factor(50_000_000_000_000_000)
.negative_impact_fee_factor(70_000_000_000_000_000)
.build(),
position_params: PositionParams::new(
100_000_000_000_000_000_000,
100_000_000_000_000_000_000,
1_000_000_000_000_000_000,
500_000_000_000_000_000,
500_000_000_000_000_000,
250_000_000_000_000_000,
),
position_impact_params: PriceImpactParams::builder()
.exponent(200_000_000_000_000_000_000)
.positive_factor(100_000_000_000)
.negative_factor(200_000_000_000)
.build(),
order_fee_params: FeeParams::builder()
.fee_receiver_factor(37_000_000_000_000_000_000)
.positive_impact_fee_factor(50_000_000_000_000_000)
.negative_impact_fee_factor(70_000_000_000_000_000)
.build(),
position_impact_distribution_params: PositionImpactDistributionParams::builder()
.distribute_factor(100_000_000_000_000_000_000)
.min_position_impact_pool_amount(1_000_000_000)
.build(),
borrowing_fee_params: BorrowingFeeParams::builder()
.receiver_factor(37_000_000_000_000_000_000)
.factor_for_long(2_820_000_000_000)
.factor_for_short(2_820_000_000_000)
.exponent_for_long(100_000_000_000_000_000_000)
.exponent_for_short(100_000_000_000_000_000_000)
.build(),
borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide::builder()
.optimal_usage_factor(75_000_000_000_000_000_000)
.base_borrowing_factor(60_000_000_000_000_000_000 / u128::from(SECONDS_PER_YEAR))
.above_optimal_usage_borrowing_factor(
150_000_000_000_000_000_000 / u128::from(SECONDS_PER_YEAR),
)
.build(),
funding_fee_params: FundingFeeParams::builder()
.exponent(100_000_000_000_000_000_000)
.funding_factor(2_000_000_000_000)
.max_factor_per_second(1_000_000_000_000)
.min_factor_per_second(30_000_000_000)
.increase_factor_per_second(790_000_000)
.decrease_factor_per_second(0)
.threshold_for_stable_funding(5_000_000_000_000_000_000)
.threshold_for_decrease_funding(0)
.build(),
reserve_factor: 10u128.pow(20),
open_interest_reserve_factor: 10u128.pow(20),
max_pnl_factors: MaxPnlFactors {
deposit: 60_000_000_000_000_000_000,
withdrawal: 30_000_000_000_000_000_000,
trader: 50_000_000_000_000_000_000,
adl: 50_000_000_000_000_000_000,
},
min_pnl_factor_after_adl: 0,
max_pool_amount: 1_000_000_000 * 10u128.pow(20),
max_pool_value_for_deposit: 1_000_000_000_000_000 * 10u128.pow(20),
max_open_interest: 1_000_000_000 * 10u128.pow(20),
min_collateral_factor_for_oi: 5 * 10u128.pow(17) / 83_000_000,
ignore_open_interest_for_usage_factor: false,
liquidation_fee_params: LiquidationFeeParams::builder()
.factor(200_000_000_000_000_000)
.receiver_factor(37_000_000_000_000_000_000)
.build(),
}
}
}
impl<T, const DECIMALS: u8> TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn now(&self) -> Instant {
self.clock.now()
}
fn just_passed_in_seconds(&mut self, clock: ClockKind) -> crate::Result<u64> {
let now = self.now();
let clock = self.clocks.entry(clock).or_insert(now);
let duration = now.saturating_duration_since(*clock);
*clock = now;
Ok(duration.as_secs())
}
fn passed_in_seconds(&self, clock: ClockKind) -> crate::Result<u64> {
let now = self.now();
let clock = self.clocks.get(&clock).unwrap_or(&now);
let duration = now.saturating_duration_since(*clock);
Ok(duration.as_secs())
}
}
impl<T, const DECIMALS: u8> BaseMarket<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
type Num = T;
type Signed = T::Signed;
type Pool = TestPool<T>;
fn liquidity_pool(&self) -> crate::Result<&Self::Pool> {
Ok(&self.primary)
}
fn claimable_fee_pool(&self) -> crate::Result<&Self::Pool> {
Ok(&self.fee)
}
fn swap_impact_pool(&self) -> crate::Result<&Self::Pool> {
Ok(&self.swap_impact)
}
fn open_interest_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
if is_long {
Ok(&self.open_interest.0)
} else {
Ok(&self.open_interest.1)
}
}
fn open_interest_in_tokens_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
if is_long {
Ok(&self.open_interest_in_tokens.0)
} else {
Ok(&self.open_interest_in_tokens.1)
}
}
fn collateral_sum_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
if is_long {
Ok(&self.collateral_sum.0)
} else {
Ok(&self.collateral_sum.1)
}
}
fn virtual_inventory_for_swaps_pool(
&self,
) -> crate::Result<Option<impl Deref<Target = Self::Pool>>> {
Ok(None::<&Self::Pool>)
}
fn virtual_inventory_for_positions_pool(
&self,
) -> crate::Result<Option<impl Deref<Target = Self::Pool>>> {
Ok(None::<&Self::Pool>)
}
fn usd_to_amount_divisor(&self) -> Self::Num {
self.value_to_amount_divisor.clone()
}
fn max_pool_amount(&self, _is_long_token: bool) -> crate::Result<Self::Num> {
Ok(self.config.max_pool_amount.clone())
}
fn pnl_factor_config(&self, kind: PnlFactorKind, _is_long: bool) -> crate::Result<Self::Num> {
let factor = match kind {
PnlFactorKind::MaxAfterDeposit => self.config.max_pnl_factors.deposit.clone(),
PnlFactorKind::MaxAfterWithdrawal => self.config.max_pnl_factors.withdrawal.clone(),
PnlFactorKind::MaxForTrader => self.config.max_pnl_factors.trader.clone(),
PnlFactorKind::ForAdl => self.config.max_pnl_factors.adl.clone(),
PnlFactorKind::MinAfterAdl => self.config.min_pnl_factor_after_adl.clone(),
};
Ok(factor)
}
fn reserve_factor(&self) -> crate::Result<Self::Num> {
Ok(self.config.reserve_factor.clone())
}
fn open_interest_reserve_factor(&self) -> crate::Result<Self::Num> {
Ok(self.config.open_interest_reserve_factor.clone())
}
fn max_open_interest(&self, _is_long: bool) -> crate::Result<Self::Num> {
Ok(self.config.max_open_interest.clone())
}
fn ignore_open_interest_for_usage_factor(&self) -> crate::Result<bool> {
Ok(self.config.ignore_open_interest_for_usage_factor)
}
}
impl<T, const DECIMALS: u8> BaseMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn liquidity_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
Ok(&mut self.primary)
}
fn claimable_fee_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
Ok(&mut self.fee)
}
fn virtual_inventory_for_swaps_pool_mut(
&mut self,
) -> crate::Result<Option<impl DerefMut<Target = Self::Pool>>> {
Ok(None::<&mut Self::Pool>)
}
}
impl<T, const DECIMALS: u8> SwapMarket<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn swap_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
Ok(self.config.swap_impact_params.clone())
}
fn swap_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
Ok(self.config.swap_fee_params.clone())
}
}
impl<T, const DECIMALS: u8> SwapMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn swap_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
Ok(&mut self.swap_impact)
}
}
impl<T, const DECIMALS: u8> LiquidityMarket<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn total_supply(&self) -> Self::Num {
self.total_supply.clone()
}
fn max_pool_value_for_deposit(&self, _is_long_token: bool) -> crate::Result<Self::Num> {
Ok(self.config.max_pool_value_for_deposit.clone())
}
}
impl<T, const DECIMALS: u8> LiquidityMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn mint(&mut self, amount: &Self::Num) -> Result<(), crate::Error> {
self.total_supply = self
.total_supply
.checked_add(amount)
.ok_or(crate::Error::Overflow)?;
Ok(())
}
fn burn(&mut self, amount: &Self::Num) -> crate::Result<()> {
self.total_supply = self
.total_supply
.checked_sub(amount)
.ok_or(crate::Error::Computation("burning market tokens"))?;
Ok(())
}
}
impl<T, const DECIMALS: u8> PositionImpactMarket<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn position_impact_pool(&self) -> crate::Result<&Self::Pool> {
Ok(&self.position_impact)
}
fn position_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
Ok(self.config.position_impact_params.clone())
}
fn position_impact_distribution_params(
&self,
) -> crate::Result<PositionImpactDistributionParams<Self::Num>> {
Ok(self.config.position_impact_distribution_params.clone())
}
fn passed_in_seconds_for_position_impact_distribution(&self) -> crate::Result<u64> {
self.passed_in_seconds(ClockKind::PriceImpactDistribution)
}
}
impl<T, const DECIMALS: u8> PositionImpactMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn position_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
Ok(&mut self.position_impact)
}
fn just_passed_in_seconds_for_position_impact_distribution(&mut self) -> crate::Result<u64> {
self.just_passed_in_seconds(ClockKind::PriceImpactDistribution)
}
}
impl<T, const DECIMALS: u8> BorrowingFeeMarket<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn borrowing_fee_params(&self) -> crate::Result<BorrowingFeeParams<Self::Num>> {
Ok(self.config.borrowing_fee_params.clone())
}
fn borrowing_factor_pool(&self) -> crate::Result<&Self::Pool> {
Ok(&self.borrowing_factor)
}
fn total_borrowing_pool(&self) -> crate::Result<&Self::Pool> {
Ok(&self.total_borrowing)
}
fn passed_in_seconds_for_borrowing(&self) -> crate::Result<u64> {
self.passed_in_seconds(ClockKind::Borrowing)
}
fn borrowing_fee_kink_model_params(
&self,
) -> crate::Result<BorrowingFeeKinkModelParams<Self::Num>> {
let for_one_side = self.config.borrowing_fee_kink_model_params.clone();
Ok(BorrowingFeeKinkModelParams::builder()
.long(for_one_side.clone())
.short(for_one_side)
.build())
}
}
impl<T, const DECIMALS: u8> BorrowingFeeMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn borrowing_factor_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
Ok(&mut self.borrowing_factor)
}
fn just_passed_in_seconds_for_borrowing(&mut self) -> crate::Result<u64> {
self.just_passed_in_seconds(ClockKind::Borrowing)
}
}
impl<T, const DECIMALS: u8> PerpMarket<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn funding_factor_per_second(&self) -> &Self::Signed {
&self.funding_factor_per_second
}
fn funding_amount_per_size_adjustment(&self) -> Self::Num {
self.funding_amount_per_size_adjustment.clone()
}
fn funding_fee_params(&self) -> crate::Result<FundingFeeParams<Self::Num>> {
Ok(self.config.funding_fee_params.clone())
}
fn funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
if is_long {
Ok(&self.funding_amount_per_size.0)
} else {
Ok(&self.funding_amount_per_size.1)
}
}
fn claimable_funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
if is_long {
Ok(&self.claimable_funding_amount_per_size.0)
} else {
Ok(&self.claimable_funding_amount_per_size.1)
}
}
fn position_params(&self) -> crate::Result<PositionParams<Self::Num>> {
Ok(self.config.position_params.clone())
}
fn order_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
Ok(self.config.order_fee_params.clone())
}
fn min_collateral_factor_for_open_interest_multiplier(
&self,
_is_long: bool,
) -> crate::Result<Self::Num> {
Ok(self.config.min_collateral_factor_for_oi.clone())
}
fn liquidation_fee_params(&self) -> crate::Result<LiquidationFeeParams<Self::Num>> {
Ok(self.config.liquidation_fee_params.clone())
}
}
impl<T, const DECIMALS: u8> PerpMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed {
&mut self.funding_factor_per_second
}
fn open_interest_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
if is_long {
Ok(&mut self.open_interest.0)
} else {
Ok(&mut self.open_interest.1)
}
}
fn open_interest_in_tokens_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool> {
if is_long {
Ok(&mut self.open_interest_in_tokens.0)
} else {
Ok(&mut self.open_interest_in_tokens.1)
}
}
fn funding_amount_per_size_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool> {
if is_long {
Ok(&mut self.funding_amount_per_size.0)
} else {
Ok(&mut self.funding_amount_per_size.1)
}
}
fn claimable_funding_amount_per_size_pool_mut(
&mut self,
is_long: bool,
) -> crate::Result<&mut Self::Pool> {
if is_long {
Ok(&mut self.claimable_funding_amount_per_size.0)
} else {
Ok(&mut self.claimable_funding_amount_per_size.1)
}
}
fn collateral_sum_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
if is_long {
Ok(&mut self.collateral_sum.0)
} else {
Ok(&mut self.collateral_sum.1)
}
}
fn total_borrowing_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
Ok(&mut self.total_borrowing)
}
fn virtual_inventory_for_positions_pool_mut(
&mut self,
) -> crate::Result<Option<impl DerefMut<Target = Self::Pool>>> {
Ok(None::<&mut Self::Pool>)
}
fn just_passed_in_seconds_for_funding(&mut self) -> crate::Result<u64> {
self.just_passed_in_seconds(ClockKind::Funding)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct TestPosition<T, const DECIMALS: u8> {
is_long: bool,
is_collateral_token_long: bool,
collateral_token_amount: T,
size_in_usd: T,
size_in_tokens: T,
borrowing_factor: T,
funding_fee_amount_per_size: T,
claimable_funding_fee_amount_per_size: (T, T),
}
impl<T: Unsigned, const DECIMALS: u8> TestPosition<T, DECIMALS>
where
T::Signed: fmt::Debug,
{
pub fn ops<'a>(
&'a mut self,
market: &'a mut TestMarket<T, DECIMALS>,
) -> TestPositionOps<'a, T, DECIMALS> {
TestPositionOps {
market,
position: self,
}
}
pub fn long(long_token_as_collateral: bool) -> Self
where
T: Default,
{
Self {
is_long: true,
is_collateral_token_long: long_token_as_collateral,
..Default::default()
}
}
pub fn short(long_token_as_collateral: bool) -> Self
where
T: Default,
{
Self {
is_long: false,
is_collateral_token_long: long_token_as_collateral,
..Default::default()
}
}
}
#[derive(Debug)]
pub struct TestPositionOps<'a, T: Unsigned, const DECIMALS: u8>
where
T::Signed: fmt::Debug,
{
market: &'a mut TestMarket<T, DECIMALS>,
position: &'a mut TestPosition<T, DECIMALS>,
}
impl<T, const DECIMALS: u8> PositionState<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
type Num = T;
type Signed = T::Signed;
fn collateral_amount(&self) -> &Self::Num {
&self.position.collateral_token_amount
}
fn size_in_usd(&self) -> &Self::Num {
&self.position.size_in_usd
}
fn size_in_tokens(&self) -> &Self::Num {
&self.position.size_in_tokens
}
fn borrowing_factor(&self) -> &Self::Num {
&self.position.borrowing_factor
}
fn funding_fee_amount_per_size(&self) -> &Self::Num {
&self.position.funding_fee_amount_per_size
}
fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num {
if is_long_collateral {
&self.position.claimable_funding_fee_amount_per_size.0
} else {
&self.position.claimable_funding_fee_amount_per_size.1
}
}
}
impl<T, const DECIMALS: u8> Position<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
type Market = TestMarket<T, DECIMALS>;
fn market(&self) -> &Self::Market {
self.market
}
fn is_long(&self) -> bool {
self.position.is_long
}
fn is_collateral_token_long(&self) -> bool {
self.position.is_collateral_token_long
}
fn are_pnl_and_collateral_tokens_the_same(&self) -> bool {
self.position.is_long == self.position.is_collateral_token_long
}
fn on_validate(&self) -> crate::Result<()> {
Ok(())
}
}
impl<T, const DECIMALS: u8> PositionMut<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn market_mut(&mut self) -> &mut Self::Market {
self.market
}
fn on_increased(&mut self) -> crate::Result<()> {
Ok(())
}
fn on_decreased(&mut self) -> crate::Result<()> {
Ok(())
}
fn on_swapped(
&mut self,
ty: DecreasePositionSwapType,
report: &crate::action::swap::SwapReport<Self::Num, <Self::Num as Unsigned>::Signed>,
) -> crate::Result<()> {
println!("swapped: ty={ty:?}, report={report:?}");
Ok(())
}
fn on_swap_error(
&mut self,
ty: DecreasePositionSwapType,
error: crate::Error,
) -> crate::Result<()> {
eprintln!("swap error: ty={ty:?}, err={error}");
Ok(())
}
}
impl<T, const DECIMALS: u8> PositionStateMut<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
where
T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
T::Signed: Num + std::fmt::Debug,
{
fn collateral_amount_mut(&mut self) -> &mut Self::Num {
&mut self.position.collateral_token_amount
}
fn size_in_usd_mut(&mut self) -> &mut Self::Num {
&mut self.position.size_in_usd
}
fn size_in_tokens_mut(&mut self) -> &mut Self::Num {
&mut self.position.size_in_tokens
}
fn borrowing_factor_mut(&mut self) -> &mut Self::Num {
&mut self.position.borrowing_factor
}
fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num {
&mut self.position.funding_fee_amount_per_size
}
fn claimable_funding_fee_amount_per_size_mut(
&mut self,
is_long_collateral: bool,
) -> &mut Self::Num {
if is_long_collateral {
&mut self.position.claimable_funding_fee_amount_per_size.0
} else {
&mut self.position.claimable_funding_fee_amount_per_size.1
}
}
}