1use std::{fmt, ops::Deref};
2
3use num_traits::{CheckedNeg, 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 BalanceExt, BaseMarket, Delta, PerpMarketMut, PerpMarketMutExt, 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) =
447 self.check_liquidatable(prices, should_validate_min_collateral_usd, false)?
448 {
449 return Err(crate::Error::Liquidatable(reason));
450 }
451
452 Ok(())
453 }
454
455 fn check_liquidatable(
459 &self,
460 prices: &Prices<Self::Num>,
461 should_validate_min_collateral_usd: bool,
462 for_liquidation: bool,
463 ) -> crate::Result<Option<LiquidatableReason>> {
464 use num_traits::{CheckedAdd, CheckedMul, CheckedSub};
465
466 let size_in_usd = self.size_in_usd();
467
468 let (pnl, _, _) = self.pnl_value(prices, size_in_usd)?;
469
470 let collateral_value = self.collateral_value(prices)?;
471 let collateral_price = self.collateral_price(prices);
472
473 let size_delta_usd = size_in_usd.to_opposite_signed()?;
474
475 let PriceImpact {
476 value: mut price_impact_value,
477 balance_change,
478 } = self.position_price_impact(&size_delta_usd, true)?;
479
480 if price_impact_value.is_negative() {
481 self.market().cap_negative_position_price_impact(
482 &size_delta_usd,
483 true,
484 &mut price_impact_value,
485 )?;
486 } else {
487 price_impact_value = Zero::zero();
488 }
489
490 let fees = self.position_fees(
491 collateral_price,
492 size_in_usd,
493 balance_change,
494 false,
496 )?;
497
498 let collateral_cost_value = fees
499 .total_cost_amount()?
500 .checked_mul(collateral_price.pick_price(false))
501 .ok_or(crate::Error::Computation(
502 "overflow calculating collateral cost value",
503 ))?;
504
505 let remaining_collateral_value = collateral_value
506 .to_signed()?
507 .checked_add(&pnl)
508 .and_then(|v| {
509 v.checked_add(&price_impact_value)?
510 .checked_sub(&collateral_cost_value.to_signed().ok()?)
511 })
512 .ok_or(crate::Error::Computation(
513 "calculating remaining collateral value",
514 ))?;
515
516 let params = self.market().position_params()?;
517
518 let collateral_factor = if for_liquidation {
519 params.min_collateral_factor_for_liquidation()
520 } else {
521 params.min_collateral_factor()
522 };
523
524 match check_collateral(
525 size_in_usd,
526 collateral_factor,
527 should_validate_min_collateral_usd.then(|| params.min_collateral_value()),
528 false,
529 &remaining_collateral_value,
530 )? {
531 CheckCollateralResult::Sufficient => Ok(None),
532 CheckCollateralResult::Zero | CheckCollateralResult::Negative => {
533 Ok(Some(LiquidatableReason::NotPositive))
534 }
535 CheckCollateralResult::MinCollateralForLeverage => {
536 Ok(Some(LiquidatableReason::MinCollateralForLeverage))
537 }
538 CheckCollateralResult::MinCollateral => Ok(Some(LiquidatableReason::MinCollateral)),
539 }
540 }
541
542 fn position_price_impact(
544 &self,
545 size_delta_usd: &Self::Signed,
546 include_virtual_inventory_impact: bool,
547 ) -> crate::Result<PriceImpact<Self::Signed>> {
548 struct ReassignedValues<T> {
549 delta_long_usd_value: T,
550 delta_short_usd_value: T,
551 }
552
553 impl<T: Zero + Clone> ReassignedValues<T> {
554 fn new(is_long: bool, size_delta_usd: &T) -> Self {
555 if is_long {
556 Self {
557 delta_long_usd_value: size_delta_usd.clone(),
558 delta_short_usd_value: Zero::zero(),
559 }
560 } else {
561 Self {
562 delta_long_usd_value: Zero::zero(),
563 delta_short_usd_value: size_delta_usd.clone(),
564 }
565 }
566 }
567 }
568
569 let usd_price = One::one();
572
573 let ReassignedValues {
574 delta_long_usd_value,
575 delta_short_usd_value,
576 } = ReassignedValues::new(self.is_long(), size_delta_usd);
577
578 let params = self.market().position_impact_params()?;
579
580 let impact = self
581 .market()
582 .open_interest()?
583 .pool_delta_with_values(
584 delta_long_usd_value.clone(),
585 delta_short_usd_value.clone(),
586 &usd_price,
587 &usd_price,
588 )?
589 .price_impact(¶ms)?;
590
591 if !impact.value.is_negative() || !include_virtual_inventory_impact {
601 return Ok(impact);
602 }
603
604 let Some(virtual_inventory) = self.market().virtual_inventory_for_positions_pool()? else {
606 return Ok(impact);
607 };
608
609 let mut leftover = virtual_inventory.checked_cancel_amounts()?;
610
611 if size_delta_usd.is_negative() {
616 let offset = size_delta_usd
617 .checked_neg()
618 .ok_or(crate::Error::Computation(
619 "calculating virtual open interest offset",
620 ))?;
621 leftover =
622 leftover.checked_apply_delta(Delta::new_both_sides(true, &offset, &offset))?;
623 }
624
625 let virtual_impact = leftover
626 .pool_delta_with_values(
627 delta_long_usd_value,
628 delta_short_usd_value,
629 &usd_price,
630 &usd_price,
631 )?
632 .price_impact(¶ms)?;
633
634 if virtual_impact.value < impact.value {
635 Ok(virtual_impact)
636 } else {
637 Ok(impact)
638 }
639 }
640
641 #[inline]
643 fn capped_positive_position_price_impact(
644 &self,
645 index_token_price: &Price<Self::Num>,
646 size_delta_usd: &Self::Signed,
647 include_virtual_inventory_impact: bool,
648 ) -> crate::Result<PriceImpact<Self::Signed>> {
649 let mut impact =
650 self.position_price_impact(size_delta_usd, include_virtual_inventory_impact)?;
651 self.market().cap_positive_position_price_impact(
652 index_token_price,
653 size_delta_usd,
654 &mut impact.value,
655 )?;
656 Ok(impact)
657 }
658
659 #[inline]
664 fn capped_position_price_impact(
665 &self,
666 index_token_price: &Price<Self::Num>,
667 size_delta_usd: &Self::Signed,
668 include_virtual_inventory_impact: bool,
669 ) -> crate::Result<(PriceImpact<Self::Signed>, Self::Num)> {
670 let mut impact = self.capped_positive_position_price_impact(
671 index_token_price,
672 size_delta_usd,
673 include_virtual_inventory_impact,
674 )?;
675 let impact_diff = self.market().cap_negative_position_price_impact(
676 size_delta_usd,
677 false,
678 &mut impact.value,
679 )?;
680 Ok((impact, impact_diff))
681 }
682
683 fn pending_borrowing_fee_value(&self) -> crate::Result<Self::Num> {
685 use crate::utils;
686 use num_traits::CheckedSub;
687
688 let latest_factor = self.market().cumulative_borrowing_factor(self.is_long())?;
689 let diff_factor = latest_factor
690 .checked_sub(self.borrowing_factor())
691 .ok_or(crate::Error::Computation("invalid latest borrowing factor"))?;
692 utils::apply_factor(self.size_in_usd(), &diff_factor)
693 .ok_or(crate::Error::Computation("calculating borrowing fee value"))
694 }
695
696 fn pending_funding_fees(&self) -> crate::Result<FundingFees<Self::Num>> {
698 let adjustment = self.market().funding_amount_per_size_adjustment();
699 let fees = FundingFees::builder()
700 .amount(
701 unpack_to_funding_amount_delta(
702 &adjustment,
703 &self.market().funding_fee_amount_per_size(
704 self.is_long(),
705 self.is_collateral_token_long(),
706 )?,
707 self.funding_fee_amount_per_size(),
708 self.size_in_usd(),
709 true,
710 )
711 .ok_or(crate::Error::Computation("calculating funding fee amount"))?,
712 )
713 .claimable_long_token_amount(
714 unpack_to_funding_amount_delta(
715 &adjustment,
716 &self
717 .market()
718 .claimable_funding_fee_amount_per_size(self.is_long(), true)?,
719 self.claimable_funding_fee_amount_per_size(true),
720 self.size_in_usd(),
721 false,
722 )
723 .ok_or(crate::Error::Computation(
724 "calculating claimable long token funding fee amount",
725 ))?,
726 )
727 .claimable_short_token_amount(
728 unpack_to_funding_amount_delta(
729 &adjustment,
730 &self
731 .market()
732 .claimable_funding_fee_amount_per_size(self.is_long(), false)?,
733 self.claimable_funding_fee_amount_per_size(false),
734 self.size_in_usd(),
735 false,
736 )
737 .ok_or(crate::Error::Computation(
738 "calculating claimable short token funding fee amount",
739 ))?,
740 )
741 .build();
742 Ok(fees)
743 }
744
745 fn position_fees(
747 &self,
748 collateral_token_price: &Price<Self::Num>,
749 size_delta_usd: &Self::Num,
750 balance_change: BalanceChange,
751 is_liquidation: bool,
752 ) -> crate::Result<PositionFees<Self::Num>> {
753 debug_assert!(!collateral_token_price.has_zero(), "must be non-zero");
754
755 let liquidation_fees = is_liquidation
756 .then(|| {
757 self.market()
760 .liquidation_fee_params()?
761 .fee(size_delta_usd, collateral_token_price)
762 })
763 .transpose()?;
764
765 let fees = self
766 .market()
767 .order_fee_params()?
768 .base_position_fees(collateral_token_price, size_delta_usd, balance_change)?
769 .set_borrowing_fees(
770 self.market().borrowing_fee_params()?.receiver_factor(),
771 collateral_token_price,
772 self.pending_borrowing_fee_value()?,
773 )?
774 .set_funding_fees(self.pending_funding_fees()?)
775 .set_liquidation_fees(liquidation_fees);
776 Ok(fees)
777 }
778}
779
780impl<const DECIMALS: u8, P: Position<DECIMALS>> PositionExt<DECIMALS> for P {}
781
782pub trait PositionMutExt<const DECIMALS: u8>: PositionMut<DECIMALS>
784where
785 Self::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>,
786{
787 fn increase(
789 &mut self,
790 prices: Prices<Self::Num>,
791 collateral_increment_amount: Self::Num,
792 size_delta_usd: Self::Num,
793 acceptable_price: Option<Self::Num>,
794 ) -> crate::Result<IncreasePosition<&mut Self, DECIMALS>>
795 where
796 Self: Sized,
797 {
798 IncreasePosition::try_new(
799 self,
800 prices,
801 collateral_increment_amount,
802 size_delta_usd,
803 acceptable_price,
804 )
805 }
806
807 fn decrease(
809 &mut self,
810 prices: Prices<Self::Num>,
811 size_delta_usd: Self::Num,
812 acceptable_price: Option<Self::Num>,
813 collateral_withdrawal_amount: Self::Num,
814 flags: DecreasePositionFlags,
815 ) -> crate::Result<DecreasePosition<&mut Self, DECIMALS>>
816 where
817 Self: Sized,
818 {
819 DecreasePosition::try_new(
820 self,
821 prices,
822 size_delta_usd,
823 acceptable_price,
824 collateral_withdrawal_amount,
825 flags,
826 )
827 }
828
829 fn update_open_interest(
831 &mut self,
832 size_delta_usd: &Self::Signed,
833 size_delta_in_tokens: &Self::Signed,
834 ) -> crate::Result<()> {
835 if size_delta_usd.is_zero() {
836 return Ok(());
837 }
838
839 let is_long_collateral = self.is_collateral_token_long();
840 let is_long = self.is_long();
841
842 self.market_mut().apply_delta_to_open_interest(
843 is_long,
844 is_long_collateral,
845 size_delta_usd,
846 )?;
847
848 let open_interest_in_tokens = self
849 .market_mut()
850 .open_interest_in_tokens_pool_mut(is_long)?;
851 if is_long_collateral {
852 open_interest_in_tokens.apply_delta_to_long_amount(size_delta_in_tokens)?;
853 } else {
854 open_interest_in_tokens.apply_delta_to_short_amount(size_delta_in_tokens)?;
855 }
856
857 Ok(())
858 }
859
860 fn update_total_borrowing(
862 &mut self,
863 next_size_in_usd: &Self::Num,
864 next_borrowing_factor: &Self::Num,
865 ) -> crate::Result<()> {
866 let is_long = self.is_long();
867 let previous = crate::utils::apply_factor(self.size_in_usd(), self.borrowing_factor())
868 .ok_or(crate::Error::Computation("calculating previous borrowing"))?;
869
870 let total_borrowing = self.market_mut().total_borrowing_pool_mut()?;
871
872 let delta = {
873 let next = crate::utils::apply_factor(next_size_in_usd, next_borrowing_factor)
874 .ok_or(crate::Error::Computation("calculating next borrowing"))?;
875 next.checked_signed_sub(previous)?
876 };
877
878 total_borrowing.apply_delta_amount(is_long, &delta)?;
879
880 Ok(())
881 }
882}
883
884impl<const DECIMALS: u8, P: PositionMut<DECIMALS>> PositionMutExt<DECIMALS> for P where
885 P::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>
886{
887}
888
889pub struct CollateralDelta<T: Unsigned> {
891 next_size_in_usd: T,
892 next_collateral_amount: T,
893 realized_pnl_value: T::Signed,
894 open_interest_delta: T::Signed,
895}
896
897impl<T: Unsigned> CollateralDelta<T> {
898 pub fn new(
900 next_size_in_usd: T,
901 next_collateral_amount: T,
902 realized_pnl_value: T::Signed,
903 open_interest_delta: T::Signed,
904 ) -> Self {
905 Self {
906 next_size_in_usd,
907 next_collateral_amount,
908 realized_pnl_value,
909 open_interest_delta,
910 }
911 }
912}
913
914#[derive(Clone, Copy)]
916pub enum WillCollateralBeSufficient<T> {
917 Sufficient(T),
919 Insufficient(T),
921}
922
923impl<T> WillCollateralBeSufficient<T> {
924 pub fn is_sufficient(&self) -> bool {
926 matches!(self, Self::Sufficient(_))
927 }
928}
929
930impl<T> Deref for WillCollateralBeSufficient<T> {
931 type Target = T;
932
933 fn deref(&self) -> &Self::Target {
934 match self {
935 Self::Sufficient(v) => v,
936 Self::Insufficient(v) => v,
937 }
938 }
939}
940
941#[derive(Debug, Clone, Copy)]
943pub enum LiquidatableReason {
944 MinCollateral,
946 NotPositive,
948 MinCollateralForLeverage,
950}
951
952impl fmt::Display for LiquidatableReason {
953 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954 match self {
955 Self::MinCollateral => write!(f, "min collateral"),
956 Self::NotPositive => write!(f, "<= 0"),
957 Self::MinCollateralForLeverage => write!(f, "min collateral for leverage"),
958 }
959 }
960}
961
962enum CheckCollateralResult {
963 Sufficient,
964 Zero,
965 Negative,
966 MinCollateralForLeverage,
967 MinCollateral,
968}
969
970fn check_collateral<T, const DECIMALS: u8>(
971 size_in_usd: &T,
972 min_collateral_factor: &T,
973 min_collateral_value: Option<&T>,
974 allow_zero_collateral: bool,
975 collateral_value: &T::Signed,
976) -> crate::Result<CheckCollateralResult>
977where
978 T: FixedPointOps<DECIMALS>,
979{
980 if collateral_value.is_negative() {
981 if min_collateral_value.is_some() {
982 Ok(CheckCollateralResult::MinCollateral)
984 } else {
985 Ok(CheckCollateralResult::Negative)
986 }
987 } else {
988 let collateral_value = collateral_value.unsigned_abs();
989
990 if let Some(min_collateral_value) = min_collateral_value {
991 if collateral_value < *min_collateral_value {
992 return Ok(CheckCollateralResult::MinCollateral);
993 }
994 }
995
996 if !allow_zero_collateral && collateral_value.is_zero() {
997 return Ok(CheckCollateralResult::Zero);
998 }
999
1000 let min_collateral_usd_for_leverage =
1001 crate::utils::apply_factor(size_in_usd, min_collateral_factor).ok_or(
1002 crate::Error::Computation("calculating min collateral usd for leverage"),
1003 )?;
1004
1005 if collateral_value < min_collateral_usd_for_leverage {
1006 return Ok(CheckCollateralResult::MinCollateralForLeverage);
1007 }
1008
1009 Ok(CheckCollateralResult::Sufficient)
1010 }
1011}
1012
1013#[derive(Debug, Clone, Copy)]
1015#[cfg_attr(
1016 feature = "anchor-lang",
1017 derive(
1018 anchor_lang::AnchorDeserialize,
1019 anchor_lang::AnchorSerialize,
1020 anchor_lang::InitSpace
1021 )
1022)]
1023#[non_exhaustive]
1024pub enum InsolventCloseStep {
1025 Pnl,
1027 Fees,
1029 Funding,
1031 Impact,
1033 Diff,
1035}