1use std::{fmt, ops::Deref};
2
3use num_traits::{One, Signed, Zero};
4
5use crate::{
6 action::{
7 decrease_position::{DecreasePosition, DecreasePositionFlags, DecreasePositionSwapType},
8 increase_position::IncreasePosition,
9 swap::SwapReport,
10 update_funding_state::unpack_to_funding_amount_delta,
11 },
12 fixed::FixedPointOps,
13 market::{
14 utils::MarketUtils, BaseMarketExt, BorrowingFeeMarket, BorrowingFeeMarketExt, PerpMarket,
15 PerpMarketExt, PositionImpactMarket,
16 },
17 num::{MulDiv, Num, Unsigned, UnsignedAbs},
18 params::fee::{FundingFees, PositionFees},
19 price::{Price, Prices},
20 Balance, BalanceExt, BaseMarket, PerpMarketMut, PnlFactorKind, Pool, PoolExt,
21};
22
23pub trait PositionState<const DECIMALS: u8> {
25 type Num: MulDiv<Signed = Self::Signed> + FixedPointOps<DECIMALS>;
27
28 type Signed: UnsignedAbs<Unsigned = Self::Num> + TryFrom<Self::Num> + Num;
30
31 fn collateral_amount(&self) -> &Self::Num;
33
34 fn size_in_usd(&self) -> &Self::Num;
36
37 fn size_in_tokens(&self) -> &Self::Num;
39
40 fn borrowing_factor(&self) -> &Self::Num;
42
43 fn funding_fee_amount_per_size(&self) -> &Self::Num;
45
46 fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num;
48}
49
50pub trait PositionStateMut<const DECIMALS: u8>: PositionState<DECIMALS> {
52 fn collateral_amount_mut(&mut self) -> &mut Self::Num;
54
55 fn size_in_usd_mut(&mut self) -> &mut Self::Num;
57
58 fn size_in_tokens_mut(&mut self) -> &mut Self::Num;
60
61 fn borrowing_factor_mut(&mut self) -> &mut Self::Num;
63
64 fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num;
66
67 fn claimable_funding_fee_amount_per_size_mut(
69 &mut self,
70 is_long_collateral: bool,
71 ) -> &mut Self::Num;
72}
73
74pub trait Position<const DECIMALS: u8>: PositionState<DECIMALS> {
76 type Market: PerpMarket<DECIMALS, Num = Self::Num, Signed = Self::Signed>;
78
79 fn market(&self) -> &Self::Market;
81
82 fn is_long(&self) -> bool;
84
85 fn is_collateral_token_long(&self) -> bool;
87
88 fn are_pnl_and_collateral_tokens_the_same(&self) -> bool;
90
91 fn on_validate(&self) -> crate::Result<()>;
93}
94
95pub trait PositionMut<const DECIMALS: u8>: Position<DECIMALS> + PositionStateMut<DECIMALS> {
97 fn market_mut(&mut self) -> &mut Self::Market;
99
100 fn on_increased(&mut self) -> crate::Result<()>;
102
103 fn on_decreased(&mut self) -> crate::Result<()>;
105
106 fn on_swapped(
108 &mut self,
109 ty: DecreasePositionSwapType,
110 report: &SwapReport<Self::Num, <Self::Num as Unsigned>::Signed>,
111 ) -> crate::Result<()>;
112
113 fn on_swap_error(
115 &mut self,
116 ty: DecreasePositionSwapType,
117 error: crate::Error,
118 ) -> crate::Result<()>;
119}
120
121impl<const DECIMALS: u8, P: PositionState<DECIMALS>> PositionState<DECIMALS> for &mut P {
122 type Num = P::Num;
123
124 type Signed = P::Signed;
125
126 fn collateral_amount(&self) -> &Self::Num {
127 (**self).collateral_amount()
128 }
129
130 fn size_in_usd(&self) -> &Self::Num {
131 (**self).size_in_usd()
132 }
133
134 fn size_in_tokens(&self) -> &Self::Num {
135 (**self).size_in_tokens()
136 }
137
138 fn borrowing_factor(&self) -> &Self::Num {
139 (**self).borrowing_factor()
140 }
141
142 fn funding_fee_amount_per_size(&self) -> &Self::Num {
143 (**self).funding_fee_amount_per_size()
144 }
145
146 fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num {
147 (**self).claimable_funding_fee_amount_per_size(is_long_collateral)
148 }
149}
150
151impl<const DECIMALS: u8, P: Position<DECIMALS>> Position<DECIMALS> for &mut P {
152 type Market = P::Market;
153
154 fn market(&self) -> &Self::Market {
155 (**self).market()
156 }
157
158 fn is_long(&self) -> bool {
159 (**self).is_long()
160 }
161
162 fn is_collateral_token_long(&self) -> bool {
163 (**self).is_collateral_token_long()
164 }
165
166 fn are_pnl_and_collateral_tokens_the_same(&self) -> bool {
167 (**self).are_pnl_and_collateral_tokens_the_same()
168 }
169
170 fn on_validate(&self) -> crate::Result<()> {
171 (**self).on_validate()
172 }
173}
174
175impl<const DECIMALS: u8, P: PositionStateMut<DECIMALS>> PositionStateMut<DECIMALS> for &mut P {
176 fn collateral_amount_mut(&mut self) -> &mut Self::Num {
177 (**self).collateral_amount_mut()
178 }
179
180 fn size_in_usd_mut(&mut self) -> &mut Self::Num {
181 (**self).size_in_usd_mut()
182 }
183
184 fn size_in_tokens_mut(&mut self) -> &mut Self::Num {
185 (**self).size_in_tokens_mut()
186 }
187
188 fn borrowing_factor_mut(&mut self) -> &mut Self::Num {
189 (**self).borrowing_factor_mut()
190 }
191
192 fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num {
193 (**self).funding_fee_amount_per_size_mut()
194 }
195
196 fn claimable_funding_fee_amount_per_size_mut(
197 &mut self,
198 is_long_collateral: bool,
199 ) -> &mut Self::Num {
200 (**self).claimable_funding_fee_amount_per_size_mut(is_long_collateral)
201 }
202}
203
204impl<const DECIMALS: u8, P: PositionMut<DECIMALS>> PositionMut<DECIMALS> for &mut P {
205 fn market_mut(&mut self) -> &mut Self::Market {
206 (**self).market_mut()
207 }
208
209 fn on_increased(&mut self) -> crate::Result<()> {
210 (**self).on_increased()
211 }
212
213 fn on_decreased(&mut self) -> crate::Result<()> {
214 (**self).on_decreased()
215 }
216
217 fn on_swapped(
218 &mut self,
219 ty: DecreasePositionSwapType,
220 report: &SwapReport<Self::Num, <Self::Num as Unsigned>::Signed>,
221 ) -> crate::Result<()> {
222 (**self).on_swapped(ty, report)
223 }
224
225 fn on_swap_error(
226 &mut self,
227 ty: DecreasePositionSwapType,
228 error: crate::Error,
229 ) -> crate::Result<()> {
230 (**self).on_swap_error(ty, error)
231 }
232}
233
234pub trait PositionStateExt<const DECIMALS: u8>: PositionState<DECIMALS> {
236 fn is_empty(&self) -> bool {
238 self.size_in_usd().is_zero()
239 && self.size_in_tokens().is_zero()
240 && self.collateral_amount().is_zero()
241 }
242}
243
244impl<const DECIMALS: u8, P: PositionState<DECIMALS> + ?Sized> PositionStateExt<DECIMALS> for P {}
245
246pub trait PositionExt<const DECIMALS: u8>: Position<DECIMALS> {
248 fn will_collateral_be_sufficient(
253 &self,
254 prices: &Prices<Self::Num>,
255 delta: &CollateralDelta<Self::Num>,
256 ) -> crate::Result<WillCollateralBeSufficient<Self::Signed>> {
257 use num_traits::{CheckedAdd, CheckedMul};
258
259 let collateral_price = self.collateral_price(prices);
260
261 let mut remaining_collateral_value = delta
262 .next_collateral_amount
263 .checked_mul(collateral_price.pick_price(false))
264 .ok_or(crate::Error::Computation(
265 "overflow calculating collateral value",
266 ))?
267 .to_signed()?;
268
269 if delta.realized_pnl_value.is_negative() {
270 remaining_collateral_value = remaining_collateral_value
271 .checked_add(&delta.realized_pnl_value)
272 .ok_or(crate::Error::Computation("adding realized pnl"))?;
273 }
274
275 if remaining_collateral_value.is_negative() {
276 return Ok(WillCollateralBeSufficient::Insufficient(
277 remaining_collateral_value,
278 ));
279 }
280
281 let min_collateral_factor = self
282 .market()
283 .min_collateral_factor_for_open_interest(&delta.open_interest_delta, self.is_long())?
284 .max(
285 self.market()
286 .position_params()?
287 .min_collateral_factor()
288 .clone(),
289 );
290
291 match check_collateral(
292 &delta.next_size_in_usd,
293 &min_collateral_factor,
294 None,
295 true,
296 &remaining_collateral_value,
297 )? {
298 CheckCollateralResult::Sufficient => Ok(WillCollateralBeSufficient::Sufficient(
299 remaining_collateral_value,
300 )),
301 CheckCollateralResult::Negative | CheckCollateralResult::MinCollateralForLeverage => {
302 Ok(WillCollateralBeSufficient::Insufficient(
303 remaining_collateral_value,
304 ))
305 }
306 CheckCollateralResult::MinCollateral | CheckCollateralResult::Zero => unreachable!(),
307 }
308 }
309
310 fn collateral_price<'a>(&self, prices: &'a Prices<Self::Num>) -> &'a Price<Self::Num> {
312 if self.is_collateral_token_long() {
313 &prices.long_token_price
314 } else {
315 &prices.short_token_price
316 }
317 }
318
319 fn collateral_value(&self, prices: &Prices<Self::Num>) -> crate::Result<Self::Num> {
321 use num_traits::CheckedMul;
322
323 let collateral_token_price = self.collateral_price(prices).pick_price(false);
324
325 let collateral_value = self
326 .collateral_amount()
327 .checked_mul(collateral_token_price)
328 .ok_or(crate::Error::Computation(
329 "overflow calculating collateral value",
330 ))?;
331
332 Ok(collateral_value)
333 }
334
335 fn pnl_value(
339 &self,
340 prices: &Prices<Self::Num>,
341 size_delta_usd: &Self::Num,
342 ) -> crate::Result<(Self::Signed, Self::Signed, Self::Num)> {
343 use num_traits::{CheckedMul, CheckedSub};
344
345 let execution_price = &prices
346 .index_token_price
347 .pick_price_for_pnl(self.is_long(), false);
348
349 let position_value: Self::Signed = self
350 .size_in_tokens()
351 .checked_mul(execution_price)
352 .ok_or(crate::Error::Computation(
353 "overflow calculating position value",
354 ))?
355 .try_into()
356 .map_err(|_| crate::Error::Convert)?;
357 let size_in_usd = self
358 .size_in_usd()
359 .clone()
360 .try_into()
361 .map_err(|_| crate::Error::Convert)?;
362 let mut total_pnl = if self.is_long() {
363 position_value.checked_sub(&size_in_usd)
364 } else {
365 size_in_usd.checked_sub(&position_value)
366 }
367 .ok_or(crate::Error::Computation("calculating total pnl"))?;
368 let uncapped_total_pnl = total_pnl.clone();
369
370 if total_pnl.is_positive() {
371 let pool_value =
372 self.market()
373 .pool_value_without_pnl_for_one_side(prices, self.is_long(), false)?;
374 let pool_pnl = self
375 .market()
376 .pnl(&prices.index_token_price, self.is_long(), true)?;
377 let capped_pool_pnl = self.market().cap_pnl(
378 self.is_long(),
379 &pool_pnl,
380 &pool_value,
381 PnlFactorKind::MaxForTrader,
382 )?;
383
384 if capped_pool_pnl != pool_pnl
387 && !capped_pool_pnl.is_negative()
388 && pool_pnl.is_positive()
389 {
390 total_pnl = capped_pool_pnl
391 .unsigned_abs()
392 .checked_mul_div_with_signed_numerator(&total_pnl, &pool_pnl.unsigned_abs())
393 .ok_or(crate::Error::Computation("calculating capped total pnl"))?;
394 }
395 }
396
397 let size_delta_in_tokens = if *self.size_in_usd() == *size_delta_usd {
398 self.size_in_tokens().clone()
399 } else if self.is_long() {
400 self.size_in_tokens()
401 .checked_mul_div_ceil(size_delta_usd, self.size_in_usd())
402 .ok_or(crate::Error::Computation(
403 "calculating size delta in tokens for long",
404 ))?
405 } else {
406 self.size_in_tokens()
407 .checked_mul_div(size_delta_usd, self.size_in_usd())
408 .ok_or(crate::Error::Computation(
409 "calculating size delta in tokens for short",
410 ))?
411 };
412
413 let pnl_usd = size_delta_in_tokens
414 .checked_mul_div_with_signed_numerator(&total_pnl, self.size_in_tokens())
415 .ok_or(crate::Error::Computation("calculating pnl_usd"))?;
416
417 let uncapped_pnl_usd = size_delta_in_tokens
418 .checked_mul_div_with_signed_numerator(&uncapped_total_pnl, self.size_in_tokens())
419 .ok_or(crate::Error::Computation("calculating uncapped_pnl_usd"))?;
420
421 Ok((pnl_usd, uncapped_pnl_usd, size_delta_in_tokens))
422 }
423
424 fn validate(
426 &self,
427 prices: &Prices<Self::Num>,
428 should_validate_min_position_size: bool,
429 should_validate_min_collateral_usd: bool,
430 ) -> crate::Result<()> {
431 if self.size_in_usd().is_zero() || self.size_in_tokens().is_zero() {
432 return Err(crate::Error::InvalidPosition(
433 "size_in_usd or size_in_tokens is zero",
434 ));
435 }
436
437 self.on_validate()?;
438
439 if should_validate_min_position_size
440 && self.size_in_usd() < self.market().position_params()?.min_position_size_usd()
441 {
442 return Err(crate::Error::InvalidPosition("size in usd too small"));
443 }
444
445 if let Some(reason) = self.check_liquidatable(prices, should_validate_min_collateral_usd)? {
446 return Err(crate::Error::Liquidatable(reason));
447 }
448
449 Ok(())
450 }
451
452 fn check_liquidatable(
456 &self,
457 prices: &Prices<Self::Num>,
458 should_validate_min_collateral_usd: bool,
459 ) -> crate::Result<Option<LiquidatableReason>> {
460 use num_traits::{CheckedAdd, CheckedMul, CheckedSub};
461
462 let size_in_usd = self.size_in_usd();
463
464 let (pnl, _, _) = self.pnl_value(prices, size_in_usd)?;
465
466 let collateral_value = self.collateral_value(prices)?;
467 let collateral_price = self.collateral_price(prices);
468
469 let size_delta_usd = size_in_usd.to_opposite_signed()?;
470
471 let mut price_impact_value = self.position_price_impact(&size_delta_usd)?;
472
473 let has_positive_impact = price_impact_value.is_positive();
474
475 if price_impact_value.is_negative() {
476 self.market().cap_negative_position_price_impact(
477 &size_delta_usd,
478 true,
479 &mut price_impact_value,
480 )?;
481 } else {
482 price_impact_value = Zero::zero();
483 }
484
485 let fees = self.position_fees(
486 collateral_price,
487 size_in_usd,
488 has_positive_impact,
489 false,
491 )?;
492
493 let collateral_cost_value = fees
494 .total_cost_amount()?
495 .checked_mul(collateral_price.pick_price(false))
496 .ok_or(crate::Error::Computation(
497 "overflow calculating collateral cost value",
498 ))?;
499
500 let remaining_collateral_value = collateral_value
501 .to_signed()?
502 .checked_add(&pnl)
503 .and_then(|v| {
504 v.checked_add(&price_impact_value)?
505 .checked_sub(&collateral_cost_value.to_signed().ok()?)
506 })
507 .ok_or(crate::Error::Computation(
508 "calculating remaining collateral value",
509 ))?;
510
511 let params = self.market().position_params()?;
512
513 match check_collateral(
514 size_in_usd,
515 params.min_collateral_factor(),
516 should_validate_min_collateral_usd.then(|| params.min_collateral_value()),
517 false,
518 &remaining_collateral_value,
519 )? {
520 CheckCollateralResult::Sufficient => Ok(None),
521 CheckCollateralResult::Zero | CheckCollateralResult::Negative => {
522 Ok(Some(LiquidatableReason::NotPositive))
523 }
524 CheckCollateralResult::MinCollateralForLeverage => {
525 Ok(Some(LiquidatableReason::MinCollateralForLeverage))
526 }
527 CheckCollateralResult::MinCollateral => Ok(Some(LiquidatableReason::MinCollateral)),
528 }
529 }
530
531 fn position_price_impact(&self, size_delta_usd: &Self::Signed) -> crate::Result<Self::Signed> {
533 struct ReassignedValues<T> {
534 delta_long_usd_value: T,
535 delta_short_usd_value: T,
536 }
537
538 impl<T: Zero + Clone> ReassignedValues<T> {
539 fn new(is_long: bool, size_delta_usd: &T) -> Self {
540 if is_long {
541 Self {
542 delta_long_usd_value: size_delta_usd.clone(),
543 delta_short_usd_value: Zero::zero(),
544 }
545 } else {
546 Self {
547 delta_long_usd_value: Zero::zero(),
548 delta_short_usd_value: size_delta_usd.clone(),
549 }
550 }
551 }
552 }
553
554 let usd_price = One::one();
557
558 let ReassignedValues {
559 delta_long_usd_value,
560 delta_short_usd_value,
561 } = ReassignedValues::new(self.is_long(), size_delta_usd);
562
563 let price_impact_value = self
564 .market()
565 .open_interest()?
566 .pool_delta_with_values(
567 delta_long_usd_value,
568 delta_short_usd_value,
569 &usd_price,
570 &usd_price,
571 )?
572 .price_impact(&self.market().position_impact_params()?)?;
573 Ok(price_impact_value)
574 }
575
576 #[inline]
578 fn capped_positive_position_price_impact(
579 &self,
580 index_token_price: &Price<Self::Num>,
581 size_delta_usd: &Self::Signed,
582 ) -> crate::Result<Self::Signed> {
583 let mut impact = self.position_price_impact(size_delta_usd)?;
584 self.market().cap_positive_position_price_impact(
585 index_token_price,
586 size_delta_usd,
587 &mut impact,
588 )?;
589 Ok(impact)
590 }
591
592 #[inline]
597 fn capped_position_price_impact(
598 &self,
599 index_token_price: &Price<Self::Num>,
600 size_delta_usd: &Self::Signed,
601 ) -> crate::Result<(Self::Signed, Self::Num)> {
602 let mut impact =
603 self.capped_positive_position_price_impact(index_token_price, size_delta_usd)?;
604 let impact_diff =
605 self.market()
606 .cap_negative_position_price_impact(size_delta_usd, false, &mut impact)?;
607 Ok((impact, impact_diff))
608 }
609
610 fn pending_borrowing_fee_value(&self) -> crate::Result<Self::Num> {
612 use crate::utils;
613 use num_traits::CheckedSub;
614
615 let latest_factor = self.market().cumulative_borrowing_factor(self.is_long())?;
616 let diff_factor = latest_factor
617 .checked_sub(self.borrowing_factor())
618 .ok_or(crate::Error::Computation("invalid latest borrowing factor"))?;
619 utils::apply_factor(self.size_in_usd(), &diff_factor)
620 .ok_or(crate::Error::Computation("calculating borrowing fee value"))
621 }
622
623 fn pending_funding_fees(&self) -> crate::Result<FundingFees<Self::Num>> {
625 let adjustment = self.market().funding_amount_per_size_adjustment();
626 let fees = FundingFees::builder()
627 .amount(
628 unpack_to_funding_amount_delta(
629 &adjustment,
630 &self.market().funding_fee_amount_per_size(
631 self.is_long(),
632 self.is_collateral_token_long(),
633 )?,
634 self.funding_fee_amount_per_size(),
635 self.size_in_usd(),
636 true,
637 )
638 .ok_or(crate::Error::Computation("calculating funding fee amount"))?,
639 )
640 .claimable_long_token_amount(
641 unpack_to_funding_amount_delta(
642 &adjustment,
643 &self
644 .market()
645 .claimable_funding_fee_amount_per_size(self.is_long(), true)?,
646 self.claimable_funding_fee_amount_per_size(true),
647 self.size_in_usd(),
648 false,
649 )
650 .ok_or(crate::Error::Computation(
651 "calculating claimable long token funding fee amount",
652 ))?,
653 )
654 .claimable_short_token_amount(
655 unpack_to_funding_amount_delta(
656 &adjustment,
657 &self
658 .market()
659 .claimable_funding_fee_amount_per_size(self.is_long(), false)?,
660 self.claimable_funding_fee_amount_per_size(false),
661 self.size_in_usd(),
662 false,
663 )
664 .ok_or(crate::Error::Computation(
665 "calculating claimable short token funding fee amount",
666 ))?,
667 )
668 .build();
669 Ok(fees)
670 }
671
672 fn position_fees(
674 &self,
675 collateral_token_price: &Price<Self::Num>,
676 size_delta_usd: &Self::Num,
677 is_positive_impact: bool,
678 is_liquidation: bool,
679 ) -> crate::Result<PositionFees<Self::Num>> {
680 debug_assert!(!collateral_token_price.has_zero(), "must be non-zero");
681
682 let liquidation_fees = is_liquidation
683 .then(|| {
684 self.market()
687 .liquidation_fee_params()?
688 .fee(size_delta_usd, collateral_token_price)
689 })
690 .transpose()?;
691
692 let fees = self
693 .market()
694 .order_fee_params()?
695 .base_position_fees(collateral_token_price, size_delta_usd, is_positive_impact)?
696 .set_borrowing_fees(
697 self.market().borrowing_fee_params()?.receiver_factor(),
698 collateral_token_price,
699 self.pending_borrowing_fee_value()?,
700 )?
701 .set_funding_fees(self.pending_funding_fees()?)
702 .set_liquidation_fees(liquidation_fees);
703 Ok(fees)
704 }
705}
706
707impl<const DECIMALS: u8, P: Position<DECIMALS>> PositionExt<DECIMALS> for P {}
708
709pub trait PositionMutExt<const DECIMALS: u8>: PositionMut<DECIMALS>
711where
712 Self::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>,
713{
714 fn increase(
716 &mut self,
717 prices: Prices<Self::Num>,
718 collateral_increment_amount: Self::Num,
719 size_delta_usd: Self::Num,
720 acceptable_price: Option<Self::Num>,
721 ) -> crate::Result<IncreasePosition<&mut Self, DECIMALS>>
722 where
723 Self: Sized,
724 {
725 IncreasePosition::try_new(
726 self,
727 prices,
728 collateral_increment_amount,
729 size_delta_usd,
730 acceptable_price,
731 )
732 }
733
734 fn decrease(
736 &mut self,
737 prices: Prices<Self::Num>,
738 size_delta_usd: Self::Num,
739 acceptable_price: Option<Self::Num>,
740 collateral_withdrawal_amount: Self::Num,
741 flags: DecreasePositionFlags,
742 ) -> crate::Result<DecreasePosition<&mut Self, DECIMALS>>
743 where
744 Self: Sized,
745 {
746 DecreasePosition::try_new(
747 self,
748 prices,
749 size_delta_usd,
750 acceptable_price,
751 collateral_withdrawal_amount,
752 flags,
753 )
754 }
755
756 fn update_open_interest(
758 &mut self,
759 size_delta_usd: &Self::Signed,
760 size_delta_in_tokens: &Self::Signed,
761 ) -> crate::Result<()> {
762 use num_traits::CheckedAdd;
763
764 if size_delta_usd.is_zero() {
765 return Ok(());
766 }
767 let is_long_collateral = self.is_collateral_token_long();
768 let is_long = self.is_long();
769 let max_open_interest = self.market().max_open_interest(is_long)?;
770
771 let open_interest = self.market_mut().open_interest_pool_mut(is_long)?;
772 if is_long_collateral {
773 open_interest.apply_delta_to_long_amount(size_delta_usd)?;
774 } else {
775 open_interest.apply_delta_to_short_amount(size_delta_usd)?;
776 }
777
778 if size_delta_usd.is_positive() {
779 let is_exceeded = open_interest
780 .long_amount()?
781 .checked_add(&open_interest.short_amount()?)
782 .map(|total| total > max_open_interest)
783 .unwrap_or(true);
784
785 if is_exceeded {
786 return Err(crate::Error::MaxOpenInterestExceeded);
787 }
788 }
789
790 let open_interest_in_tokens = self
791 .market_mut()
792 .open_interest_in_tokens_pool_mut(is_long)?;
793 if is_long_collateral {
794 open_interest_in_tokens.apply_delta_to_long_amount(size_delta_in_tokens)?;
795 } else {
796 open_interest_in_tokens.apply_delta_to_short_amount(size_delta_in_tokens)?;
797 }
798
799 Ok(())
800 }
801
802 fn update_total_borrowing(
804 &mut self,
805 next_size_in_usd: &Self::Num,
806 next_borrowing_factor: &Self::Num,
807 ) -> crate::Result<()> {
808 let is_long = self.is_long();
809 let previous = crate::utils::apply_factor(self.size_in_usd(), self.borrowing_factor())
810 .ok_or(crate::Error::Computation("calculating previous borrowing"))?;
811
812 let total_borrowing = self.market_mut().total_borrowing_pool_mut()?;
813
814 let delta = {
815 let next = crate::utils::apply_factor(next_size_in_usd, next_borrowing_factor)
816 .ok_or(crate::Error::Computation("calculating next borrowing"))?;
817 next.checked_signed_sub(previous)?
818 };
819
820 total_borrowing.apply_delta_amount(is_long, &delta)?;
821
822 Ok(())
823 }
824}
825
826impl<const DECIMALS: u8, P: PositionMut<DECIMALS>> PositionMutExt<DECIMALS> for P where
827 P::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>
828{
829}
830
831pub struct CollateralDelta<T: Unsigned> {
833 next_size_in_usd: T,
834 next_collateral_amount: T,
835 realized_pnl_value: T::Signed,
836 open_interest_delta: T::Signed,
837}
838
839impl<T: Unsigned> CollateralDelta<T> {
840 pub fn new(
842 next_size_in_usd: T,
843 next_collateral_amount: T,
844 realized_pnl_value: T::Signed,
845 open_interest_delta: T::Signed,
846 ) -> Self {
847 Self {
848 next_size_in_usd,
849 next_collateral_amount,
850 realized_pnl_value,
851 open_interest_delta,
852 }
853 }
854}
855
856#[derive(Clone, Copy)]
858pub enum WillCollateralBeSufficient<T> {
859 Sufficient(T),
861 Insufficient(T),
863}
864
865impl<T> WillCollateralBeSufficient<T> {
866 pub fn is_sufficient(&self) -> bool {
868 matches!(self, Self::Sufficient(_))
869 }
870}
871
872impl<T> Deref for WillCollateralBeSufficient<T> {
873 type Target = T;
874
875 fn deref(&self) -> &Self::Target {
876 match self {
877 Self::Sufficient(v) => v,
878 Self::Insufficient(v) => v,
879 }
880 }
881}
882
883#[derive(Debug, Clone, Copy)]
885pub enum LiquidatableReason {
886 MinCollateral,
888 NotPositive,
890 MinCollateralForLeverage,
892}
893
894impl fmt::Display for LiquidatableReason {
895 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
896 match self {
897 Self::MinCollateral => write!(f, "min collateral"),
898 Self::NotPositive => write!(f, "<= 0"),
899 Self::MinCollateralForLeverage => write!(f, "min collateral for leverage"),
900 }
901 }
902}
903
904enum CheckCollateralResult {
905 Sufficient,
906 Zero,
907 Negative,
908 MinCollateralForLeverage,
909 MinCollateral,
910}
911
912fn check_collateral<T, const DECIMALS: u8>(
913 size_in_usd: &T,
914 min_collateral_factor: &T,
915 min_collateral_value: Option<&T>,
916 allow_zero_collateral: bool,
917 collateral_value: &T::Signed,
918) -> crate::Result<CheckCollateralResult>
919where
920 T: FixedPointOps<DECIMALS>,
921{
922 if collateral_value.is_negative() {
923 if min_collateral_value.is_some() {
924 Ok(CheckCollateralResult::MinCollateral)
926 } else {
927 Ok(CheckCollateralResult::Negative)
928 }
929 } else {
930 let collateral_value = collateral_value.unsigned_abs();
931
932 if let Some(min_collateral_value) = min_collateral_value {
933 if collateral_value < *min_collateral_value {
934 return Ok(CheckCollateralResult::MinCollateral);
935 }
936 }
937
938 if !allow_zero_collateral && collateral_value.is_zero() {
939 return Ok(CheckCollateralResult::Zero);
940 }
941
942 let min_collateral_usd_for_leverage =
943 crate::utils::apply_factor(size_in_usd, min_collateral_factor).ok_or(
944 crate::Error::Computation("calculating min collateral usd for leverage"),
945 )?;
946
947 if collateral_value < min_collateral_usd_for_leverage {
948 return Ok(CheckCollateralResult::MinCollateralForLeverage);
949 }
950
951 Ok(CheckCollateralResult::Sufficient)
952 }
953}
954
955#[derive(Debug, Clone, Copy)]
957#[cfg_attr(
958 feature = "anchor-lang",
959 derive(
960 anchor_lang::AnchorDeserialize,
961 anchor_lang::AnchorSerialize,
962 anchor_lang::InitSpace
963 )
964)]
965#[non_exhaustive]
966pub enum InsolventCloseStep {
967 Pnl,
969 Fees,
971 Funding,
973 Impact,
975 Diff,
977}