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 size_delta_in_tokens(&self, size_delta_usd: &Self::Num) -> crate::Result<Self::Num> {
338 let size_delta_in_tokens = if *self.size_in_usd() == *size_delta_usd {
339 self.size_in_tokens().clone()
340 } else if self.is_long() {
341 self.size_in_tokens()
342 .checked_mul_div_ceil(size_delta_usd, self.size_in_usd())
343 .ok_or(crate::Error::Computation(
344 "calculating size delta in tokens for long",
345 ))?
346 } else {
347 self.size_in_tokens()
348 .checked_mul_div(size_delta_usd, self.size_in_usd())
349 .ok_or(crate::Error::Computation(
350 "calculating size delta in tokens for short",
351 ))?
352 };
353
354 Ok(size_delta_in_tokens)
355 }
356
357 fn pnl_value(
361 &self,
362 prices: &Prices<Self::Num>,
363 size_delta_usd: &Self::Num,
364 ) -> crate::Result<(Self::Signed, Self::Signed, Self::Num)> {
365 use num_traits::{CheckedMul, CheckedSub};
366
367 let execution_price = &prices
368 .index_token_price
369 .pick_price_for_pnl(self.is_long(), false);
370
371 let position_value: Self::Signed = self
372 .size_in_tokens()
373 .checked_mul(execution_price)
374 .ok_or(crate::Error::Computation(
375 "overflow calculating position value",
376 ))?
377 .try_into()
378 .map_err(|_| crate::Error::Convert)?;
379 let size_in_usd = self
380 .size_in_usd()
381 .clone()
382 .try_into()
383 .map_err(|_| crate::Error::Convert)?;
384 let mut total_pnl = if self.is_long() {
385 position_value.checked_sub(&size_in_usd)
386 } else {
387 size_in_usd.checked_sub(&position_value)
388 }
389 .ok_or(crate::Error::Computation("calculating total pnl"))?;
390 let uncapped_total_pnl = total_pnl.clone();
391
392 if total_pnl.is_positive() {
393 let pool_value =
394 self.market()
395 .pool_value_without_pnl_for_one_side(prices, self.is_long(), false)?;
396 let pool_pnl = self
397 .market()
398 .pnl(&prices.index_token_price, self.is_long(), true)?;
399 let capped_pool_pnl = self.market().cap_pnl(
400 self.is_long(),
401 &pool_pnl,
402 &pool_value,
403 PnlFactorKind::MaxForTrader,
404 )?;
405
406 if capped_pool_pnl != pool_pnl
409 && !capped_pool_pnl.is_negative()
410 && pool_pnl.is_positive()
411 {
412 total_pnl = capped_pool_pnl
413 .unsigned_abs()
414 .checked_mul_div_with_signed_numerator(&total_pnl, &pool_pnl.unsigned_abs())
415 .ok_or(crate::Error::Computation("calculating capped total pnl"))?;
416 }
417 }
418
419 let size_delta_in_tokens = self.size_delta_in_tokens(size_delta_usd)?;
420
421 let pnl_usd = size_delta_in_tokens
422 .checked_mul_div_with_signed_numerator(&total_pnl, self.size_in_tokens())
423 .ok_or(crate::Error::Computation("calculating pnl_usd"))?;
424
425 let uncapped_pnl_usd = size_delta_in_tokens
426 .checked_mul_div_with_signed_numerator(&uncapped_total_pnl, self.size_in_tokens())
427 .ok_or(crate::Error::Computation("calculating uncapped_pnl_usd"))?;
428
429 Ok((pnl_usd, uncapped_pnl_usd, size_delta_in_tokens))
430 }
431
432 fn validate(
434 &self,
435 prices: &Prices<Self::Num>,
436 should_validate_min_position_size: bool,
437 should_validate_min_collateral_usd: bool,
438 ) -> crate::Result<()> {
439 if self.size_in_usd().is_zero() || self.size_in_tokens().is_zero() {
440 return Err(crate::Error::InvalidPosition(
441 "size_in_usd or size_in_tokens is zero",
442 ));
443 }
444
445 self.on_validate()?;
446
447 if should_validate_min_position_size
448 && self.size_in_usd() < self.market().position_params()?.min_position_size_usd()
449 {
450 return Err(crate::Error::InvalidPosition("size in usd too small"));
451 }
452
453 if let Some(reason) =
454 self.check_liquidatable(prices, should_validate_min_collateral_usd, false)?
455 {
456 return Err(crate::Error::Liquidatable(reason));
457 }
458
459 Ok(())
460 }
461
462 fn check_liquidatable(
466 &self,
467 prices: &Prices<Self::Num>,
468 should_validate_min_collateral_usd: bool,
469 for_liquidation: bool,
470 ) -> crate::Result<Option<LiquidatableReason>> {
471 use num_traits::{CheckedAdd, CheckedMul, CheckedSub};
472
473 let size_in_usd = self.size_in_usd();
474
475 let (pnl, _, _) = self.pnl_value(prices, size_in_usd)?;
476
477 let collateral_value = self.collateral_value(prices)?;
478 let collateral_price = self.collateral_price(prices);
479
480 let size_delta_usd = size_in_usd.to_opposite_signed()?;
481
482 let PriceImpact {
483 value: mut price_impact_value,
484 balance_change,
485 } = self.position_price_impact(&size_delta_usd, true)?;
486
487 if price_impact_value.is_negative() {
488 self.market().cap_negative_position_price_impact(
489 &size_delta_usd,
490 true,
491 &mut price_impact_value,
492 )?;
493 } else {
494 price_impact_value = Zero::zero();
495 }
496
497 let fees = self.position_fees(
498 collateral_price,
499 size_in_usd,
500 balance_change,
501 false,
503 )?;
504
505 let collateral_cost_value = fees
506 .total_cost_amount()?
507 .checked_mul(collateral_price.pick_price(false))
508 .ok_or(crate::Error::Computation(
509 "overflow calculating collateral cost value",
510 ))?;
511
512 let remaining_collateral_value = collateral_value
513 .to_signed()?
514 .checked_add(&pnl)
515 .and_then(|v| {
516 v.checked_add(&price_impact_value)?
517 .checked_sub(&collateral_cost_value.to_signed().ok()?)
518 })
519 .ok_or(crate::Error::Computation(
520 "calculating remaining collateral value",
521 ))?;
522
523 let params = self.market().position_params()?;
524
525 let collateral_factor = if for_liquidation {
526 params.min_collateral_factor_for_liquidation()
527 } else {
528 params.min_collateral_factor()
529 };
530
531 match check_collateral(
532 size_in_usd,
533 collateral_factor,
534 should_validate_min_collateral_usd.then(|| params.min_collateral_value()),
535 false,
536 &remaining_collateral_value,
537 )? {
538 CheckCollateralResult::Sufficient => Ok(None),
539 CheckCollateralResult::Zero | CheckCollateralResult::Negative => {
540 Ok(Some(LiquidatableReason::NotPositive))
541 }
542 CheckCollateralResult::MinCollateralForLeverage => {
543 Ok(Some(LiquidatableReason::MinCollateralForLeverage))
544 }
545 CheckCollateralResult::MinCollateral => Ok(Some(LiquidatableReason::MinCollateral)),
546 }
547 }
548
549 fn position_price_impact(
551 &self,
552 size_delta_usd: &Self::Signed,
553 include_virtual_inventory_impact: bool,
554 ) -> crate::Result<PriceImpact<Self::Signed>> {
555 struct ReassignedValues<T> {
556 delta_long_usd_value: T,
557 delta_short_usd_value: T,
558 }
559
560 impl<T: Zero + Clone> ReassignedValues<T> {
561 fn new(is_long: bool, size_delta_usd: &T) -> Self {
562 if is_long {
563 Self {
564 delta_long_usd_value: size_delta_usd.clone(),
565 delta_short_usd_value: Zero::zero(),
566 }
567 } else {
568 Self {
569 delta_long_usd_value: Zero::zero(),
570 delta_short_usd_value: size_delta_usd.clone(),
571 }
572 }
573 }
574 }
575
576 let usd_price = One::one();
579
580 let ReassignedValues {
581 delta_long_usd_value,
582 delta_short_usd_value,
583 } = ReassignedValues::new(self.is_long(), size_delta_usd);
584
585 let params = self.market().position_impact_params()?;
586
587 let impact = self
588 .market()
589 .open_interest()?
590 .pool_delta_with_values(
591 delta_long_usd_value.clone(),
592 delta_short_usd_value.clone(),
593 &usd_price,
594 &usd_price,
595 )?
596 .price_impact(¶ms)?;
597
598 if !impact.value.is_negative() || !include_virtual_inventory_impact {
608 return Ok(impact);
609 }
610
611 let Some(virtual_inventory) = self.market().virtual_inventory_for_positions_pool()? else {
613 return Ok(impact);
614 };
615
616 let mut leftover = virtual_inventory.checked_cancel_amounts()?;
617
618 if size_delta_usd.is_negative() {
623 let offset = size_delta_usd
624 .checked_neg()
625 .ok_or(crate::Error::Computation(
626 "calculating virtual open interest offset",
627 ))?;
628 leftover =
629 leftover.checked_apply_delta(Delta::new_both_sides(true, &offset, &offset))?;
630 }
631
632 let virtual_impact = leftover
633 .pool_delta_with_values(
634 delta_long_usd_value,
635 delta_short_usd_value,
636 &usd_price,
637 &usd_price,
638 )?
639 .price_impact(¶ms)?;
640
641 if virtual_impact.value < impact.value {
642 Ok(virtual_impact)
643 } else {
644 Ok(impact)
645 }
646 }
647
648 #[inline]
650 fn capped_positive_position_price_impact(
651 &self,
652 index_token_price: &Price<Self::Num>,
653 size_delta_usd: &Self::Signed,
654 include_virtual_inventory_impact: bool,
655 ) -> crate::Result<PriceImpact<Self::Signed>> {
656 let mut impact =
657 self.position_price_impact(size_delta_usd, include_virtual_inventory_impact)?;
658 self.market().cap_positive_position_price_impact(
659 index_token_price,
660 size_delta_usd,
661 &mut impact.value,
662 )?;
663 Ok(impact)
664 }
665
666 #[inline]
671 fn capped_position_price_impact(
672 &self,
673 index_token_price: &Price<Self::Num>,
674 size_delta_usd: &Self::Signed,
675 include_virtual_inventory_impact: bool,
676 ) -> crate::Result<(PriceImpact<Self::Signed>, Self::Num)> {
677 let mut impact = self.capped_positive_position_price_impact(
678 index_token_price,
679 size_delta_usd,
680 include_virtual_inventory_impact,
681 )?;
682 let impact_diff = self.market().cap_negative_position_price_impact(
683 size_delta_usd,
684 false,
685 &mut impact.value,
686 )?;
687 Ok((impact, impact_diff))
688 }
689
690 fn pending_borrowing_fee_value(&self) -> crate::Result<Self::Num> {
692 use crate::utils;
693 use num_traits::CheckedSub;
694
695 let latest_factor = self.market().cumulative_borrowing_factor(self.is_long())?;
696 let diff_factor = latest_factor
697 .checked_sub(self.borrowing_factor())
698 .ok_or(crate::Error::Computation("invalid latest borrowing factor"))?;
699 utils::apply_factor(self.size_in_usd(), &diff_factor)
700 .ok_or(crate::Error::Computation("calculating borrowing fee value"))
701 }
702
703 fn pending_funding_fees(&self) -> crate::Result<FundingFees<Self::Num>> {
705 let adjustment = self.market().funding_amount_per_size_adjustment();
706 let fees = FundingFees::builder()
707 .amount(
708 unpack_to_funding_amount_delta(
709 &adjustment,
710 &self.market().funding_fee_amount_per_size(
711 self.is_long(),
712 self.is_collateral_token_long(),
713 )?,
714 self.funding_fee_amount_per_size(),
715 self.size_in_usd(),
716 true,
717 )
718 .ok_or(crate::Error::Computation("calculating funding fee amount"))?,
719 )
720 .claimable_long_token_amount(
721 unpack_to_funding_amount_delta(
722 &adjustment,
723 &self
724 .market()
725 .claimable_funding_fee_amount_per_size(self.is_long(), true)?,
726 self.claimable_funding_fee_amount_per_size(true),
727 self.size_in_usd(),
728 false,
729 )
730 .ok_or(crate::Error::Computation(
731 "calculating claimable long token funding fee amount",
732 ))?,
733 )
734 .claimable_short_token_amount(
735 unpack_to_funding_amount_delta(
736 &adjustment,
737 &self
738 .market()
739 .claimable_funding_fee_amount_per_size(self.is_long(), false)?,
740 self.claimable_funding_fee_amount_per_size(false),
741 self.size_in_usd(),
742 false,
743 )
744 .ok_or(crate::Error::Computation(
745 "calculating claimable short token funding fee amount",
746 ))?,
747 )
748 .build();
749 Ok(fees)
750 }
751
752 fn position_fees(
754 &self,
755 collateral_token_price: &Price<Self::Num>,
756 size_delta_usd: &Self::Num,
757 balance_change: BalanceChange,
758 is_liquidation: bool,
759 ) -> crate::Result<PositionFees<Self::Num>> {
760 debug_assert!(!collateral_token_price.has_zero(), "must be non-zero");
761
762 let liquidation_fees = is_liquidation
763 .then(|| {
764 self.market()
767 .liquidation_fee_params()?
768 .fee(size_delta_usd, collateral_token_price)
769 })
770 .transpose()?;
771
772 let fees = self
773 .market()
774 .order_fee_params()?
775 .base_position_fees(collateral_token_price, size_delta_usd, balance_change)?
776 .set_borrowing_fees(
777 self.market().borrowing_fee_params()?.receiver_factor(),
778 collateral_token_price,
779 self.pending_borrowing_fee_value()?,
780 )?
781 .set_funding_fees(self.pending_funding_fees()?)
782 .set_liquidation_fees(liquidation_fees);
783 Ok(fees)
784 }
785}
786
787impl<const DECIMALS: u8, P: Position<DECIMALS>> PositionExt<DECIMALS> for P {}
788
789pub trait PositionMutExt<const DECIMALS: u8>: PositionMut<DECIMALS>
791where
792 Self::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>,
793{
794 fn increase(
796 &mut self,
797 prices: Prices<Self::Num>,
798 collateral_increment_amount: Self::Num,
799 size_delta_usd: Self::Num,
800 acceptable_price: Option<Self::Num>,
801 ) -> crate::Result<IncreasePosition<&mut Self, DECIMALS>>
802 where
803 Self: Sized,
804 {
805 IncreasePosition::try_new(
806 self,
807 prices,
808 collateral_increment_amount,
809 size_delta_usd,
810 acceptable_price,
811 )
812 }
813
814 fn decrease(
816 &mut self,
817 prices: Prices<Self::Num>,
818 size_delta_usd: Self::Num,
819 acceptable_price: Option<Self::Num>,
820 collateral_withdrawal_amount: Self::Num,
821 flags: DecreasePositionFlags,
822 ) -> crate::Result<DecreasePosition<&mut Self, DECIMALS>>
823 where
824 Self: Sized,
825 {
826 DecreasePosition::try_new(
827 self,
828 prices,
829 size_delta_usd,
830 acceptable_price,
831 collateral_withdrawal_amount,
832 flags,
833 )
834 }
835
836 fn update_open_interest(
838 &mut self,
839 size_delta_usd: &Self::Signed,
840 size_delta_in_tokens: &Self::Signed,
841 ) -> crate::Result<()> {
842 if size_delta_usd.is_zero() {
843 return Ok(());
844 }
845
846 let is_long_collateral = self.is_collateral_token_long();
847 let is_long = self.is_long();
848
849 self.market_mut().apply_delta_to_open_interest(
850 is_long,
851 is_long_collateral,
852 size_delta_usd,
853 )?;
854
855 let open_interest_in_tokens = self
856 .market_mut()
857 .open_interest_in_tokens_pool_mut(is_long)?;
858 if is_long_collateral {
859 open_interest_in_tokens.apply_delta_to_long_amount(size_delta_in_tokens)?;
860 } else {
861 open_interest_in_tokens.apply_delta_to_short_amount(size_delta_in_tokens)?;
862 }
863
864 Ok(())
865 }
866
867 fn update_total_borrowing(
869 &mut self,
870 next_size_in_usd: &Self::Num,
871 next_borrowing_factor: &Self::Num,
872 ) -> crate::Result<()> {
873 let is_long = self.is_long();
874 let previous = crate::utils::apply_factor(self.size_in_usd(), self.borrowing_factor())
875 .ok_or(crate::Error::Computation("calculating previous borrowing"))?;
876
877 let total_borrowing = self.market_mut().total_borrowing_pool_mut()?;
878
879 let delta = {
880 let next = crate::utils::apply_factor(next_size_in_usd, next_borrowing_factor)
881 .ok_or(crate::Error::Computation("calculating next borrowing"))?;
882 next.checked_signed_sub(previous)?
883 };
884
885 total_borrowing.apply_delta_amount(is_long, &delta)?;
886
887 Ok(())
888 }
889}
890
891impl<const DECIMALS: u8, P: PositionMut<DECIMALS>> PositionMutExt<DECIMALS> for P where
892 P::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>
893{
894}
895
896pub struct CollateralDelta<T: Unsigned> {
898 next_size_in_usd: T,
899 next_collateral_amount: T,
900 realized_pnl_value: T::Signed,
901 open_interest_delta: T::Signed,
902}
903
904impl<T: Unsigned> CollateralDelta<T> {
905 pub fn new(
907 next_size_in_usd: T,
908 next_collateral_amount: T,
909 realized_pnl_value: T::Signed,
910 open_interest_delta: T::Signed,
911 ) -> Self {
912 Self {
913 next_size_in_usd,
914 next_collateral_amount,
915 realized_pnl_value,
916 open_interest_delta,
917 }
918 }
919}
920
921#[derive(Clone, Copy)]
923pub enum WillCollateralBeSufficient<T> {
924 Sufficient(T),
926 Insufficient(T),
928}
929
930impl<T> WillCollateralBeSufficient<T> {
931 pub fn is_sufficient(&self) -> bool {
933 matches!(self, Self::Sufficient(_))
934 }
935}
936
937impl<T> Deref for WillCollateralBeSufficient<T> {
938 type Target = T;
939
940 fn deref(&self) -> &Self::Target {
941 match self {
942 Self::Sufficient(v) => v,
943 Self::Insufficient(v) => v,
944 }
945 }
946}
947
948#[derive(Debug, Clone, Copy)]
950pub enum LiquidatableReason {
951 MinCollateral,
953 NotPositive,
955 MinCollateralForLeverage,
957}
958
959impl fmt::Display for LiquidatableReason {
960 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
961 match self {
962 Self::MinCollateral => write!(f, "min collateral"),
963 Self::NotPositive => write!(f, "<= 0"),
964 Self::MinCollateralForLeverage => write!(f, "min collateral for leverage"),
965 }
966 }
967}
968
969enum CheckCollateralResult {
970 Sufficient,
971 Zero,
972 Negative,
973 MinCollateralForLeverage,
974 MinCollateral,
975}
976
977fn check_collateral<T, const DECIMALS: u8>(
978 size_in_usd: &T,
979 min_collateral_factor: &T,
980 min_collateral_value: Option<&T>,
981 allow_zero_collateral: bool,
982 collateral_value: &T::Signed,
983) -> crate::Result<CheckCollateralResult>
984where
985 T: FixedPointOps<DECIMALS>,
986{
987 if collateral_value.is_negative() {
988 if min_collateral_value.is_some() {
989 Ok(CheckCollateralResult::MinCollateral)
991 } else {
992 Ok(CheckCollateralResult::Negative)
993 }
994 } else {
995 let collateral_value = collateral_value.unsigned_abs();
996
997 if let Some(min_collateral_value) = min_collateral_value {
998 if collateral_value < *min_collateral_value {
999 return Ok(CheckCollateralResult::MinCollateral);
1000 }
1001 }
1002
1003 if !allow_zero_collateral && collateral_value.is_zero() {
1004 return Ok(CheckCollateralResult::Zero);
1005 }
1006
1007 let min_collateral_usd_for_leverage =
1008 crate::utils::apply_factor(size_in_usd, min_collateral_factor).ok_or(
1009 crate::Error::Computation("calculating min collateral usd for leverage"),
1010 )?;
1011
1012 if collateral_value < min_collateral_usd_for_leverage {
1013 return Ok(CheckCollateralResult::MinCollateralForLeverage);
1014 }
1015
1016 Ok(CheckCollateralResult::Sufficient)
1017 }
1018}
1019
1020#[derive(Debug, Clone, Copy)]
1022#[cfg_attr(
1023 feature = "anchor-lang",
1024 derive(
1025 anchor_lang::AnchorDeserialize,
1026 anchor_lang::AnchorSerialize,
1027 anchor_lang::InitSpace
1028 )
1029)]
1030#[non_exhaustive]
1031pub enum InsolventCloseStep {
1032 Pnl,
1034 Fees,
1036 Funding,
1038 Impact,
1040 Diff,
1042}