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#[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#[derive(Debug, Clone)]
106pub struct MaxPnlFactors<T> {
107 pub deposit: T,
109 pub withdrawal: T,
111 pub trader: T,
113 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#[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 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 pub fn move_clock_forward(&mut self, duration: Duration) {
190 self.clock.move_forward(duration);
191 }
192}
193
194impl TestMarket<u64, 9> {
195 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 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#[derive(Debug, Clone)]
226pub struct TestMarketConfig<T, const DECIMALS: u8> {
227 pub swap_impact_params: PriceImpactParams<T>,
229 pub swap_fee_params: FeeParams<T>,
231 pub position_params: PositionParams<T>,
233 pub position_impact_params: PriceImpactParams<T>,
235 pub order_fee_params: FeeParams<T>,
237 pub position_impact_distribution_params: PositionImpactDistributionParams<T>,
239 pub borrowing_fee_params: BorrowingFeeParams<T>,
241 pub borrowing_fee_kink_model_params: BorrowingFeeKinkModelParamsForOneSide<T>,
243 pub funding_fee_params: FundingFeeParams<T>,
245 pub reserve_factor: T,
247 pub open_interest_reserve_factor: T,
249 pub max_pnl_factors: MaxPnlFactors<T>,
251 pub min_pnl_factor_after_adl: T,
253 pub max_pool_amount: T,
255 pub max_pool_value_for_deposit: T,
257 pub max_open_interest: T,
259 pub min_collateral_factor_for_oi: T,
261 pub ignore_open_interest_for_usage_factor: bool,
263 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_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_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#[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 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 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 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#[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}