gmsol_model/
test.rs

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