gmsol_model/
test.rs

1#![allow(clippy::arithmetic_side_effects)]
2
3use std::{
4    collections::HashMap,
5    fmt,
6    time::{Duration, Instant},
7};
8
9use crate::{
10    action::decrease_position::DecreasePositionSwapType,
11    clock::ClockKind,
12    fixed::FixedPointOps,
13    market::{
14        BaseMarket, BorrowingFeeMarketMut, LiquidityMarket, LiquidityMarketMut, PerpMarket,
15        PnlFactorKind, PositionImpactMarket, SwapMarket,
16    },
17    num::{MulDiv, Num, Unsigned, UnsignedAbs},
18    params::{
19        fee::{
20            BorrowingFeeKinkModelParams, BorrowingFeeKinkModelParamsForOneSide, BorrowingFeeParams,
21            FundingFeeParams, LiquidationFeeParams,
22        },
23        position::PositionImpactDistributionParams,
24        FeeParams, PositionParams, PriceImpactParams,
25    },
26    pool::{Balance, Delta, Pool},
27    position::Position,
28    BaseMarketMut, BorrowingFeeMarket, PerpMarketMut, PositionImpactMarketMut, PositionMut,
29    PositionState, PositionStateMut, SwapMarketMut,
30};
31use num_traits::{CheckedSub, Signed};
32
33/// Test Pool.
34#[derive(Debug, Default, Clone, Copy)]
35pub struct TestPool<T> {
36    long_amount: T,
37    short_amount: T,
38}
39
40impl<T> Balance for TestPool<T>
41where
42    T: MulDiv + Num + CheckedSub,
43{
44    type Num = T;
45
46    type Signed = T::Signed;
47
48    fn long_amount(&self) -> crate::Result<Self::Num> {
49        Ok(self.long_amount.clone())
50    }
51
52    fn short_amount(&self) -> crate::Result<Self::Num> {
53        Ok(self.short_amount.clone())
54    }
55}
56
57impl<T> Pool for TestPool<T>
58where
59    T: MulDiv + Num + CheckedSub,
60{
61    fn checked_apply_delta(&self, delta: Delta<&Self::Signed>) -> crate::Result<Self> {
62        let mut ans = self.clone();
63        if let Some(amount) = delta.long() {
64            ans.apply_delta_to_long_amount(amount)?;
65        }
66        if let Some(amount) = delta.short() {
67            ans.apply_delta_to_short_amount(amount)?;
68        }
69        Ok(ans)
70    }
71
72    fn apply_delta_to_long_amount(&mut self, delta: &Self::Signed) -> Result<(), crate::Error> {
73        if delta.is_positive() {
74            self.long_amount = self
75                .long_amount
76                .checked_add(&delta.unsigned_abs())
77                .ok_or(crate::Error::Overflow)?;
78        } else {
79            self.long_amount = self
80                .long_amount
81                .checked_sub(&delta.unsigned_abs())
82                .ok_or(crate::Error::Computation("decreasing long amount"))?;
83        }
84        Ok(())
85    }
86
87    fn apply_delta_to_short_amount(&mut self, delta: &Self::Signed) -> Result<(), crate::Error> {
88        if delta.is_positive() {
89            self.short_amount = self
90                .short_amount
91                .checked_add(&delta.unsigned_abs())
92                .ok_or(crate::Error::Overflow)?;
93        } else {
94            self.short_amount = self
95                .short_amount
96                .checked_sub(&delta.unsigned_abs())
97                .ok_or(crate::Error::Computation("decreasing short amount"))?;
98        }
99        Ok(())
100    }
101}
102
103/// Max PnL Factors.
104#[derive(Debug, Clone)]
105pub struct MaxPnlFactors<T> {
106    /// For deposit.
107    pub deposit: T,
108    /// For withdrawal.
109    pub withdrawal: T,
110    /// For trader.
111    pub trader: T,
112    /// For ADL.
113    pub adl: T,
114}
115
116#[derive(Debug, Clone, Copy, Default)]
117struct Clock {
118    advanced: Duration,
119}
120
121impl Clock {
122    fn now(&self) -> Instant {
123        Instant::now() + self.advanced
124    }
125
126    fn move_forward(&mut self, duration: Duration) {
127        self.advanced += duration;
128    }
129}
130
131/// Test Market.
132#[derive(Debug, Clone)]
133pub struct TestMarket<T: Unsigned, const DECIMALS: u8> {
134    config: TestMarketConfig<T, DECIMALS>,
135    total_supply: T,
136    value_to_amount_divisor: T,
137    funding_amount_per_size_adjustment: T,
138    primary: TestPool<T>,
139    swap_impact: TestPool<T>,
140    fee: TestPool<T>,
141    open_interest: (TestPool<T>, TestPool<T>),
142    open_interest_in_tokens: (TestPool<T>, TestPool<T>),
143    position_impact: TestPool<T>,
144    borrowing_factor: TestPool<T>,
145    funding_factor_per_second: T::Signed,
146    funding_amount_per_size: (TestPool<T>, TestPool<T>),
147    claimable_funding_amount_per_size: (TestPool<T>, TestPool<T>),
148    collateral_sum: (TestPool<T>, TestPool<T>),
149    total_borrowing: TestPool<T>,
150    clock: Clock,
151    clocks: HashMap<ClockKind, Instant>,
152}
153
154impl<T: Unsigned, const DECIMALS: u8> TestMarket<T, DECIMALS> {
155    /// Create a new test market.
156    fn new(
157        value_to_amount_divisor: T,
158        funding_amount_per_size_adjustment: T,
159        config: TestMarketConfig<T, DECIMALS>,
160    ) -> Self
161    where
162        T: Default,
163        T::Signed: Default,
164    {
165        Self {
166            config,
167            total_supply: Default::default(),
168            value_to_amount_divisor,
169            funding_amount_per_size_adjustment,
170            primary: Default::default(),
171            swap_impact: Default::default(),
172            fee: Default::default(),
173            open_interest: Default::default(),
174            open_interest_in_tokens: Default::default(),
175            position_impact: Default::default(),
176            borrowing_factor: Default::default(),
177            funding_factor_per_second: Default::default(),
178            funding_amount_per_size: Default::default(),
179            claimable_funding_amount_per_size: Default::default(),
180            collateral_sum: Default::default(),
181            total_borrowing: Default::default(),
182            clocks: Default::default(),
183            clock: Default::default(),
184        }
185    }
186
187    /// Move the clock forward.
188    pub fn move_clock_forward(&mut self, duration: Duration) {
189        self.clock.move_forward(duration);
190    }
191}
192
193impl TestMarket<u64, 9> {
194    /// Create a new [`TestMarket`] with config.
195    pub fn with_config(config: TestMarketConfig<u64, 9>) -> Self {
196        Self::new(1, 10_000, config)
197    }
198}
199
200#[cfg(feature = "u128")]
201impl TestMarket<u128, 20> {
202    /// Create a new [`TestMarket`] with config.
203    pub fn with_config(config: TestMarketConfig<u128, 20>) -> Self {
204        Self::new(10u128.pow(20 - 9), 10u128.pow(10), config)
205    }
206}
207
208impl Default for TestMarket<u64, 9> {
209    fn default() -> Self {
210        Self::with_config(Default::default())
211    }
212}
213
214#[cfg(feature = "u128")]
215impl Default for TestMarket<u128, 20> {
216    fn default() -> Self {
217        Self::with_config(Default::default())
218    }
219}
220
221const SECONDS_PER_YEAR: u64 = 365 * 24 * 3600;
222
223/// Test Market Config.
224#[derive(Debug, Clone)]
225pub struct TestMarketConfig<T, const DECIMALS: u8> {
226    /// Swap impact params.
227    pub swap_impact_params: PriceImpactParams<T>,
228    /// Swap fee params.
229    pub swap_fee_params: FeeParams<T>,
230    /// Position params.
231    pub position_params: PositionParams<T>,
232    /// Position impact params.
233    pub position_impact_params: PriceImpactParams<T>,
234    /// Order fee params.
235    pub order_fee_params: FeeParams<T>,
236    /// Position impact distribution params.
237    pub position_impact_distribution_params: PositionImpactDistributionParams<T>,
238    /// Borrowing fee params.
239    pub borrowing_fee_params: BorrowingFeeParams<T>,
240    /// Borrowing fee kink model params.
241    pub borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide<T>,
242    /// Funding fee params.
243    pub funding_fee_params: FundingFeeParams<T>,
244    /// Reserve factor.
245    pub reserve_factor: T,
246    /// Open interest reserve factor.
247    pub open_interest_reserve_factor: T,
248    /// Max PnL factors.
249    pub max_pnl_factors: MaxPnlFactors<T>,
250    /// Min PnL factors after ADL.
251    pub min_pnl_factor_after_adl: T,
252    /// Max pool amount.
253    pub max_pool_amount: T,
254    /// Max pool value for deposit.
255    pub max_pool_value_for_deposit: T,
256    /// Max open interest.
257    pub max_open_interest: T,
258    /// Min collateral factor for OI.
259    pub min_collateral_factor_for_oi: T,
260    /// Ignore open interest for usage factor.
261    pub ignore_open_interest_for_usage_factor: bool,
262    /// Liquidation fee params.
263    pub liquidation_fee_params: LiquidationFeeParams<T>,
264}
265
266impl Default for TestMarketConfig<u64, 9> {
267    fn default() -> Self {
268        Self {
269            swap_impact_params: PriceImpactParams::builder()
270                .exponent(2_000_000_000)
271                .positive_factor(4)
272                .negative_factor(8)
273                .build(),
274            swap_fee_params: FeeParams::builder()
275                .fee_receiver_factor(370_000_000)
276                .positive_impact_fee_factor(500_000)
277                .negative_impact_fee_factor(700_000)
278                .build(),
279            position_params: PositionParams::new(
280                1_000_000_000,
281                1_000_000_000,
282                10_000_000,
283                5_000_000,
284                5_000_000,
285                2_500_000,
286            ),
287            position_impact_params: PriceImpactParams::builder()
288                .exponent(2_000_000_000)
289                .positive_factor(1)
290                .negative_factor(2)
291                .build(),
292            order_fee_params: FeeParams::builder()
293                .fee_receiver_factor(370_000_000)
294                .positive_impact_fee_factor(500_000)
295                .negative_impact_fee_factor(700_000)
296                .build(),
297            position_impact_distribution_params: PositionImpactDistributionParams::builder()
298                .distribute_factor(1_000_000_000)
299                .min_position_impact_pool_amount(1_000_000_000)
300                .build(),
301            borrowing_fee_params: BorrowingFeeParams::builder()
302                .receiver_factor(370_000_000)
303                .factor_for_long(28)
304                .factor_for_short(28)
305                .exponent_for_long(1_000_000_000)
306                .exponent_for_short(1_000_000_000)
307                .build(),
308            borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide::builder()
309                .optimal_usage_factor(750_000_000)
310                .base_borrowing_factor(600_000_000 / SECONDS_PER_YEAR)
311                .above_optimal_usage_borrowing_factor(1_500_000_000 / SECONDS_PER_YEAR)
312                .build(),
313            funding_fee_params: FundingFeeParams::builder()
314                .exponent(1_000_000_000)
315                .funding_factor(20)
316                .max_factor_per_second(10)
317                .min_factor_per_second(1)
318                .increase_factor_per_second(10)
319                .decrease_factor_per_second(0)
320                .threshold_for_stable_funding(50_000_000)
321                .threshold_for_decrease_funding(0)
322                .build(),
323            reserve_factor: 1_000_000_000,
324            max_pnl_factors: MaxPnlFactors {
325                deposit: 600_000_000,
326                withdrawal: 300_000_000,
327                trader: 500_000_000,
328                adl: 500_000_000,
329            },
330            min_pnl_factor_after_adl: 0,
331            open_interest_reserve_factor: 1_000_000_000,
332            max_pool_amount: 1_000_000_000 * 1_000_000_000,
333            max_pool_value_for_deposit: u64::MAX,
334            max_open_interest: u64::MAX,
335            // min collateral factor of 0.005 when open interest is $83,000,000
336            min_collateral_factor_for_oi: 5 * 10u64.pow(6) / 83_000_000,
337            ignore_open_interest_for_usage_factor: false,
338            liquidation_fee_params: LiquidationFeeParams::builder()
339                .factor(2_000_000)
340                .receiver_factor(370_000_000)
341                .build(),
342        }
343    }
344}
345
346#[cfg(feature = "u128")]
347impl Default for TestMarketConfig<u128, 20> {
348    fn default() -> Self {
349        Self {
350            swap_impact_params: PriceImpactParams::builder()
351                .exponent(200_000_000_000_000_000_000)
352                .positive_factor(400_000_000_000)
353                .negative_factor(800_000_000_000)
354                .build(),
355            swap_fee_params: FeeParams::builder()
356                .fee_receiver_factor(37_000_000_000_000_000_000)
357                .positive_impact_fee_factor(50_000_000_000_000_000)
358                .negative_impact_fee_factor(70_000_000_000_000_000)
359                .build(),
360            position_params: PositionParams::new(
361                100_000_000_000_000_000_000,
362                100_000_000_000_000_000_000,
363                1_000_000_000_000_000_000,
364                500_000_000_000_000_000,
365                500_000_000_000_000_000,
366                250_000_000_000_000_000,
367            ),
368            position_impact_params: PriceImpactParams::builder()
369                .exponent(200_000_000_000_000_000_000)
370                .positive_factor(100_000_000_000)
371                .negative_factor(200_000_000_000)
372                .build(),
373            order_fee_params: FeeParams::builder()
374                .fee_receiver_factor(37_000_000_000_000_000_000)
375                .positive_impact_fee_factor(50_000_000_000_000_000)
376                .negative_impact_fee_factor(70_000_000_000_000_000)
377                .build(),
378            position_impact_distribution_params: PositionImpactDistributionParams::builder()
379                .distribute_factor(100_000_000_000_000_000_000)
380                .min_position_impact_pool_amount(1_000_000_000)
381                .build(),
382            borrowing_fee_params: BorrowingFeeParams::builder()
383                .receiver_factor(37_000_000_000_000_000_000)
384                .factor_for_long(2_820_000_000_000)
385                .factor_for_short(2_820_000_000_000)
386                .exponent_for_long(100_000_000_000_000_000_000)
387                .exponent_for_short(100_000_000_000_000_000_000)
388                .build(),
389            borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide::builder()
390                .optimal_usage_factor(75_000_000_000_000_000_000)
391                .base_borrowing_factor(60_000_000_000_000_000_000 / u128::from(SECONDS_PER_YEAR))
392                .above_optimal_usage_borrowing_factor(
393                    150_000_000_000_000_000_000 / u128::from(SECONDS_PER_YEAR),
394                )
395                .build(),
396            funding_fee_params: FundingFeeParams::builder()
397                .exponent(100_000_000_000_000_000_000)
398                .funding_factor(2_000_000_000_000)
399                .max_factor_per_second(1_000_000_000_000)
400                .min_factor_per_second(30_000_000_000)
401                .increase_factor_per_second(790_000_000)
402                .decrease_factor_per_second(0)
403                .threshold_for_stable_funding(5_000_000_000_000_000_000)
404                .threshold_for_decrease_funding(0)
405                .build(),
406            reserve_factor: 10u128.pow(20),
407            open_interest_reserve_factor: 10u128.pow(20),
408            max_pnl_factors: MaxPnlFactors {
409                deposit: 60_000_000_000_000_000_000,
410                withdrawal: 30_000_000_000_000_000_000,
411                trader: 50_000_000_000_000_000_000,
412                adl: 50_000_000_000_000_000_000,
413            },
414            min_pnl_factor_after_adl: 0,
415            max_pool_amount: 1_000_000_000 * 10u128.pow(20),
416            max_pool_value_for_deposit: 1_000_000_000_000_000 * 10u128.pow(20),
417            max_open_interest: 1_000_000_000 * 10u128.pow(20),
418            // min collateral factor of 0.005 when open interest is $83,000,000
419            min_collateral_factor_for_oi: 5 * 10u128.pow(17) / 83_000_000,
420            ignore_open_interest_for_usage_factor: false,
421            liquidation_fee_params: LiquidationFeeParams::builder()
422                .factor(200_000_000_000_000_000)
423                .receiver_factor(37_000_000_000_000_000_000)
424                .build(),
425        }
426    }
427}
428
429impl<T, const DECIMALS: u8> TestMarket<T, DECIMALS>
430where
431    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
432    T::Signed: Num + std::fmt::Debug,
433{
434    fn now(&self) -> Instant {
435        self.clock.now()
436    }
437
438    fn just_passed_in_seconds(&mut self, clock: ClockKind) -> crate::Result<u64> {
439        let now = self.now();
440        let clock = self.clocks.entry(clock).or_insert(now);
441        let duration = now.saturating_duration_since(*clock);
442        *clock = now;
443        Ok(duration.as_secs())
444    }
445
446    fn passed_in_seconds(&self, clock: ClockKind) -> crate::Result<u64> {
447        let now = self.now();
448        let clock = self.clocks.get(&clock).unwrap_or(&now);
449        let duration = now.saturating_duration_since(*clock);
450        Ok(duration.as_secs())
451    }
452}
453
454impl<T, const DECIMALS: u8> BaseMarket<DECIMALS> for TestMarket<T, DECIMALS>
455where
456    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
457    T::Signed: Num + std::fmt::Debug,
458{
459    type Num = T;
460
461    type Signed = T::Signed;
462
463    type Pool = TestPool<T>;
464
465    fn liquidity_pool(&self) -> crate::Result<&Self::Pool> {
466        Ok(&self.primary)
467    }
468
469    fn claimable_fee_pool(&self) -> crate::Result<&Self::Pool> {
470        Ok(&self.fee)
471    }
472
473    fn swap_impact_pool(&self) -> crate::Result<&Self::Pool> {
474        Ok(&self.swap_impact)
475    }
476
477    fn open_interest_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
478        if is_long {
479            Ok(&self.open_interest.0)
480        } else {
481            Ok(&self.open_interest.1)
482        }
483    }
484
485    fn open_interest_in_tokens_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
486        if is_long {
487            Ok(&self.open_interest_in_tokens.0)
488        } else {
489            Ok(&self.open_interest_in_tokens.1)
490        }
491    }
492
493    fn collateral_sum_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
494        if is_long {
495            Ok(&self.collateral_sum.0)
496        } else {
497            Ok(&self.collateral_sum.1)
498        }
499    }
500
501    fn usd_to_amount_divisor(&self) -> Self::Num {
502        self.value_to_amount_divisor.clone()
503    }
504
505    fn max_pool_amount(&self, _is_long_token: bool) -> crate::Result<Self::Num> {
506        Ok(self.config.max_pool_amount.clone())
507    }
508
509    fn pnl_factor_config(&self, kind: PnlFactorKind, _is_long: bool) -> crate::Result<Self::Num> {
510        let factor = match kind {
511            PnlFactorKind::MaxAfterDeposit => self.config.max_pnl_factors.deposit.clone(),
512            PnlFactorKind::MaxAfterWithdrawal => self.config.max_pnl_factors.withdrawal.clone(),
513            PnlFactorKind::MaxForTrader => self.config.max_pnl_factors.trader.clone(),
514            PnlFactorKind::ForAdl => self.config.max_pnl_factors.adl.clone(),
515            PnlFactorKind::MinAfterAdl => self.config.min_pnl_factor_after_adl.clone(),
516        };
517        Ok(factor)
518    }
519
520    fn reserve_factor(&self) -> crate::Result<Self::Num> {
521        Ok(self.config.reserve_factor.clone())
522    }
523
524    fn open_interest_reserve_factor(&self) -> crate::Result<Self::Num> {
525        Ok(self.config.open_interest_reserve_factor.clone())
526    }
527
528    fn max_open_interest(&self, _is_long: bool) -> crate::Result<Self::Num> {
529        Ok(self.config.max_open_interest.clone())
530    }
531
532    fn ignore_open_interest_for_usage_factor(&self) -> crate::Result<bool> {
533        Ok(self.config.ignore_open_interest_for_usage_factor)
534    }
535}
536
537impl<T, const DECIMALS: u8> BaseMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
538where
539    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
540    T::Signed: Num + std::fmt::Debug,
541{
542    fn liquidity_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
543        Ok(&mut self.primary)
544    }
545
546    fn claimable_fee_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
547        Ok(&mut self.fee)
548    }
549}
550
551impl<T, const DECIMALS: u8> SwapMarket<DECIMALS> for TestMarket<T, DECIMALS>
552where
553    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
554    T::Signed: Num + std::fmt::Debug,
555{
556    fn swap_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
557        Ok(self.config.swap_impact_params.clone())
558    }
559
560    fn swap_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
561        Ok(self.config.swap_fee_params.clone())
562    }
563}
564
565impl<T, const DECIMALS: u8> SwapMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
566where
567    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
568    T::Signed: Num + std::fmt::Debug,
569{
570    fn swap_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
571        Ok(&mut self.swap_impact)
572    }
573}
574
575impl<T, const DECIMALS: u8> LiquidityMarket<DECIMALS> for TestMarket<T, DECIMALS>
576where
577    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
578    T::Signed: Num + std::fmt::Debug,
579{
580    fn total_supply(&self) -> Self::Num {
581        self.total_supply.clone()
582    }
583
584    fn max_pool_value_for_deposit(&self, _is_long_token: bool) -> crate::Result<Self::Num> {
585        Ok(self.config.max_pool_value_for_deposit.clone())
586    }
587}
588
589impl<T, const DECIMALS: u8> LiquidityMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
590where
591    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
592    T::Signed: Num + std::fmt::Debug,
593{
594    fn mint(&mut self, amount: &Self::Num) -> Result<(), crate::Error> {
595        self.total_supply = self
596            .total_supply
597            .checked_add(amount)
598            .ok_or(crate::Error::Overflow)?;
599        Ok(())
600    }
601
602    fn burn(&mut self, amount: &Self::Num) -> crate::Result<()> {
603        self.total_supply = self
604            .total_supply
605            .checked_sub(amount)
606            .ok_or(crate::Error::Computation("burning market tokens"))?;
607        Ok(())
608    }
609}
610
611impl<T, const DECIMALS: u8> PositionImpactMarket<DECIMALS> for TestMarket<T, DECIMALS>
612where
613    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
614    T::Signed: Num + std::fmt::Debug,
615{
616    fn position_impact_pool(&self) -> crate::Result<&Self::Pool> {
617        Ok(&self.position_impact)
618    }
619
620    fn position_impact_params(&self) -> crate::Result<PriceImpactParams<Self::Num>> {
621        Ok(self.config.position_impact_params.clone())
622    }
623
624    fn position_impact_distribution_params(
625        &self,
626    ) -> crate::Result<PositionImpactDistributionParams<Self::Num>> {
627        Ok(self.config.position_impact_distribution_params.clone())
628    }
629
630    fn passed_in_seconds_for_position_impact_distribution(&self) -> crate::Result<u64> {
631        self.passed_in_seconds(ClockKind::PriceImpactDistribution)
632    }
633}
634
635impl<T, const DECIMALS: u8> PositionImpactMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
636where
637    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
638    T::Signed: Num + std::fmt::Debug,
639{
640    fn position_impact_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
641        Ok(&mut self.position_impact)
642    }
643
644    fn just_passed_in_seconds_for_position_impact_distribution(&mut self) -> crate::Result<u64> {
645        self.just_passed_in_seconds(ClockKind::PriceImpactDistribution)
646    }
647}
648
649impl<T, const DECIMALS: u8> BorrowingFeeMarket<DECIMALS> for TestMarket<T, DECIMALS>
650where
651    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
652    T::Signed: Num + std::fmt::Debug,
653{
654    fn borrowing_fee_params(&self) -> crate::Result<BorrowingFeeParams<Self::Num>> {
655        Ok(self.config.borrowing_fee_params.clone())
656    }
657
658    fn borrowing_factor_pool(&self) -> crate::Result<&Self::Pool> {
659        Ok(&self.borrowing_factor)
660    }
661
662    fn total_borrowing_pool(&self) -> crate::Result<&Self::Pool> {
663        Ok(&self.total_borrowing)
664    }
665
666    fn passed_in_seconds_for_borrowing(&self) -> crate::Result<u64> {
667        self.passed_in_seconds(ClockKind::Borrowing)
668    }
669
670    fn borrowing_fee_kink_model_params(
671        &self,
672    ) -> crate::Result<BorrowingFeeKinkModelParams<Self::Num>> {
673        let for_one_side = self.config.borrowing_fee_kink_model_params.clone();
674        Ok(BorrowingFeeKinkModelParams::builder()
675            .long(for_one_side.clone())
676            .short(for_one_side)
677            .build())
678    }
679}
680
681impl<T, const DECIMALS: u8> BorrowingFeeMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
682where
683    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
684    T::Signed: Num + std::fmt::Debug,
685{
686    fn borrowing_factor_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
687        Ok(&mut self.borrowing_factor)
688    }
689
690    fn just_passed_in_seconds_for_borrowing(&mut self) -> crate::Result<u64> {
691        self.just_passed_in_seconds(ClockKind::Borrowing)
692    }
693}
694
695impl<T, const DECIMALS: u8> PerpMarket<DECIMALS> for TestMarket<T, DECIMALS>
696where
697    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
698    T::Signed: Num + std::fmt::Debug,
699{
700    fn funding_factor_per_second(&self) -> &Self::Signed {
701        &self.funding_factor_per_second
702    }
703
704    fn funding_amount_per_size_adjustment(&self) -> Self::Num {
705        self.funding_amount_per_size_adjustment.clone()
706    }
707
708    fn funding_fee_params(&self) -> crate::Result<FundingFeeParams<Self::Num>> {
709        Ok(self.config.funding_fee_params.clone())
710    }
711
712    fn funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
713        if is_long {
714            Ok(&self.funding_amount_per_size.0)
715        } else {
716            Ok(&self.funding_amount_per_size.1)
717        }
718    }
719
720    fn claimable_funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
721        if is_long {
722            Ok(&self.claimable_funding_amount_per_size.0)
723        } else {
724            Ok(&self.claimable_funding_amount_per_size.1)
725        }
726    }
727
728    fn position_params(&self) -> crate::Result<PositionParams<Self::Num>> {
729        Ok(self.config.position_params.clone())
730    }
731
732    fn order_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
733        Ok(self.config.order_fee_params.clone())
734    }
735
736    fn min_collateral_factor_for_open_interest_multiplier(
737        &self,
738        _is_long: bool,
739    ) -> crate::Result<Self::Num> {
740        Ok(self.config.min_collateral_factor_for_oi.clone())
741    }
742
743    fn liquidation_fee_params(&self) -> crate::Result<LiquidationFeeParams<Self::Num>> {
744        Ok(self.config.liquidation_fee_params.clone())
745    }
746}
747
748impl<T, const DECIMALS: u8> PerpMarketMut<DECIMALS> for TestMarket<T, DECIMALS>
749where
750    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
751    T::Signed: Num + std::fmt::Debug,
752{
753    fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed {
754        &mut self.funding_factor_per_second
755    }
756
757    fn open_interest_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
758        if is_long {
759            Ok(&mut self.open_interest.0)
760        } else {
761            Ok(&mut self.open_interest.1)
762        }
763    }
764
765    fn open_interest_in_tokens_pool_mut(
766        &mut self,
767        is_long: bool,
768    ) -> crate::Result<&mut Self::Pool> {
769        if is_long {
770            Ok(&mut self.open_interest_in_tokens.0)
771        } else {
772            Ok(&mut self.open_interest_in_tokens.1)
773        }
774    }
775
776    fn funding_amount_per_size_pool_mut(
777        &mut self,
778        is_long: bool,
779    ) -> crate::Result<&mut Self::Pool> {
780        if is_long {
781            Ok(&mut self.funding_amount_per_size.0)
782        } else {
783            Ok(&mut self.funding_amount_per_size.1)
784        }
785    }
786
787    fn claimable_funding_amount_per_size_pool_mut(
788        &mut self,
789        is_long: bool,
790    ) -> crate::Result<&mut Self::Pool> {
791        if is_long {
792            Ok(&mut self.claimable_funding_amount_per_size.0)
793        } else {
794            Ok(&mut self.claimable_funding_amount_per_size.1)
795        }
796    }
797
798    fn collateral_sum_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
799        if is_long {
800            Ok(&mut self.collateral_sum.0)
801        } else {
802            Ok(&mut self.collateral_sum.1)
803        }
804    }
805
806    fn total_borrowing_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
807        Ok(&mut self.total_borrowing)
808    }
809
810    fn just_passed_in_seconds_for_funding(&mut self) -> crate::Result<u64> {
811        self.just_passed_in_seconds(ClockKind::Funding)
812    }
813}
814
815/// Test Position
816#[derive(Debug, Clone, Copy, Default)]
817pub struct TestPosition<T, const DECIMALS: u8> {
818    is_long: bool,
819    is_collateral_token_long: bool,
820    collateral_token_amount: T,
821    size_in_usd: T,
822    size_in_tokens: T,
823    borrowing_factor: T,
824    funding_fee_amount_per_size: T,
825    claimable_funding_fee_amount_per_size: (T, T),
826}
827
828impl<T: Unsigned, const DECIMALS: u8> TestPosition<T, DECIMALS>
829where
830    T::Signed: fmt::Debug,
831{
832    /// Create a [`TestPositionOps`] for ops.
833    pub fn ops<'a>(
834        &'a mut self,
835        market: &'a mut TestMarket<T, DECIMALS>,
836    ) -> TestPositionOps<'a, T, DECIMALS> {
837        TestPositionOps {
838            market,
839            position: self,
840        }
841    }
842
843    /// Create an empty long position.
844    pub fn long(long_token_as_collateral: bool) -> Self
845    where
846        T: Default,
847    {
848        Self {
849            is_long: true,
850            is_collateral_token_long: long_token_as_collateral,
851            ..Default::default()
852        }
853    }
854
855    /// Create an empty short position.
856    pub fn short(long_token_as_collateral: bool) -> Self
857    where
858        T: Default,
859    {
860        Self {
861            is_long: false,
862            is_collateral_token_long: long_token_as_collateral,
863            ..Default::default()
864        }
865    }
866}
867
868/// Test Position.
869#[derive(Debug)]
870pub struct TestPositionOps<'a, T: Unsigned, const DECIMALS: u8>
871where
872    T::Signed: fmt::Debug,
873{
874    market: &'a mut TestMarket<T, DECIMALS>,
875    position: &'a mut TestPosition<T, DECIMALS>,
876}
877
878impl<T, const DECIMALS: u8> PositionState<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
879where
880    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
881    T::Signed: Num + std::fmt::Debug,
882{
883    type Num = T;
884
885    type Signed = T::Signed;
886
887    fn collateral_amount(&self) -> &Self::Num {
888        &self.position.collateral_token_amount
889    }
890
891    fn size_in_usd(&self) -> &Self::Num {
892        &self.position.size_in_usd
893    }
894
895    fn size_in_tokens(&self) -> &Self::Num {
896        &self.position.size_in_tokens
897    }
898
899    fn borrowing_factor(&self) -> &Self::Num {
900        &self.position.borrowing_factor
901    }
902
903    fn funding_fee_amount_per_size(&self) -> &Self::Num {
904        &self.position.funding_fee_amount_per_size
905    }
906
907    fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num {
908        if is_long_collateral {
909            &self.position.claimable_funding_fee_amount_per_size.0
910        } else {
911            &self.position.claimable_funding_fee_amount_per_size.1
912        }
913    }
914}
915
916impl<T, const DECIMALS: u8> Position<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
917where
918    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
919    T::Signed: Num + std::fmt::Debug,
920{
921    type Market = TestMarket<T, DECIMALS>;
922
923    fn market(&self) -> &Self::Market {
924        self.market
925    }
926
927    fn is_long(&self) -> bool {
928        self.position.is_long
929    }
930
931    fn is_collateral_token_long(&self) -> bool {
932        self.position.is_collateral_token_long
933    }
934
935    fn are_pnl_and_collateral_tokens_the_same(&self) -> bool {
936        self.position.is_long == self.position.is_collateral_token_long
937    }
938
939    fn on_validate(&self) -> crate::Result<()> {
940        Ok(())
941    }
942}
943
944impl<T, const DECIMALS: u8> PositionMut<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
945where
946    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
947    T::Signed: Num + std::fmt::Debug,
948{
949    fn market_mut(&mut self) -> &mut Self::Market {
950        self.market
951    }
952
953    fn on_increased(&mut self) -> crate::Result<()> {
954        Ok(())
955    }
956
957    fn on_decreased(&mut self) -> crate::Result<()> {
958        Ok(())
959    }
960
961    fn on_swapped(
962        &mut self,
963        ty: DecreasePositionSwapType,
964        report: &crate::action::swap::SwapReport<Self::Num, <Self::Num as Unsigned>::Signed>,
965    ) -> crate::Result<()> {
966        println!("swapped: ty={ty}, report={report:?}");
967        Ok(())
968    }
969
970    fn on_swap_error(
971        &mut self,
972        ty: DecreasePositionSwapType,
973        error: crate::Error,
974    ) -> crate::Result<()> {
975        eprintln!("swap error: ty={ty}, err={error}");
976        Ok(())
977    }
978}
979
980impl<T, const DECIMALS: u8> PositionStateMut<DECIMALS> for TestPositionOps<'_, T, DECIMALS>
981where
982    T: CheckedSub + fmt::Display + FixedPointOps<DECIMALS>,
983    T::Signed: Num + std::fmt::Debug,
984{
985    fn collateral_amount_mut(&mut self) -> &mut Self::Num {
986        &mut self.position.collateral_token_amount
987    }
988
989    fn size_in_usd_mut(&mut self) -> &mut Self::Num {
990        &mut self.position.size_in_usd
991    }
992
993    fn size_in_tokens_mut(&mut self) -> &mut Self::Num {
994        &mut self.position.size_in_tokens
995    }
996
997    fn borrowing_factor_mut(&mut self) -> &mut Self::Num {
998        &mut self.position.borrowing_factor
999    }
1000
1001    fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num {
1002        &mut self.position.funding_fee_amount_per_size
1003    }
1004
1005    fn claimable_funding_fee_amount_per_size_mut(
1006        &mut self,
1007        is_long_collateral: bool,
1008    ) -> &mut Self::Num {
1009        if is_long_collateral {
1010            &mut self.position.claimable_funding_fee_amount_per_size.0
1011        } else {
1012            &mut self.position.claimable_funding_fee_amount_per_size.1
1013        }
1014    }
1015}