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#[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#[derive(Debug, Clone)]
105pub struct MaxPnlFactors<T> {
106 pub deposit: T,
108 pub withdrawal: T,
110 pub trader: T,
112 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#[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 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 pub fn move_clock_forward(&mut self, duration: Duration) {
189 self.clock.move_forward(duration);
190 }
191}
192
193impl TestMarket<u64, 9> {
194 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 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#[derive(Debug, Clone)]
225pub struct TestMarketConfig<T, const DECIMALS: u8> {
226 pub swap_impact_params: PriceImpactParams<T>,
228 pub swap_fee_params: FeeParams<T>,
230 pub position_params: PositionParams<T>,
232 pub position_impact_params: PriceImpactParams<T>,
234 pub order_fee_params: FeeParams<T>,
236 pub position_impact_distribution_params: PositionImpactDistributionParams<T>,
238 pub borrowing_fee_params: BorrowingFeeParams<T>,
240 pub borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide<T>,
242 pub funding_fee_params: FundingFeeParams<T>,
244 pub reserve_factor: T,
246 pub open_interest_reserve_factor: T,
248 pub max_pnl_factors: MaxPnlFactors<T>,
250 pub min_pnl_factor_after_adl: T,
252 pub max_pool_amount: T,
254 pub max_pool_value_for_deposit: T,
256 pub max_open_interest: T,
258 pub min_collateral_factor_for_oi: T,
260 pub ignore_open_interest_for_usage_factor: bool,
262 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_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_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#[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 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 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 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#[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}