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