gmsol_sdk/position/
mod.rs1use gmsol_model::{
2 num::Unsigned, num_traits::Zero, price::Prices, PerpMarket, PerpMarketExt, Position,
3 PositionExt, PositionState,
4};
5use gmsol_programs::model::PositionModel;
6use status::PositionStatus;
7
8use crate::constants;
9
10pub mod status;
12
13pub trait PositionCalculations {
15 fn status(&self, prices: &Prices<u128>) -> crate::Result<PositionStatus>;
17}
18
19impl PositionCalculations for PositionModel {
20 fn status(&self, prices: &Prices<u128>) -> crate::Result<PositionStatus> {
21 let collateral_value = self.collateral_value(prices)?;
23
24 let position_size_in_tokens = self.size_in_tokens();
26 let position_size_in_usd = self.size_in_usd();
27 let _position_size_in_usd_real = position_size_in_tokens
28 .checked_mul(prices.index_token_price.max)
29 .ok_or(gmsol_model::Error::Computation(
30 "calculating position size in usd real",
31 ))?;
32 let (pending_pnl_value, _uncapped_pnl_value, _size_delta_in_tokens) =
33 self.pnl_value(prices, position_size_in_usd)?;
34 let entry_price = position_size_in_usd
35 .checked_div(*position_size_in_tokens)
36 .ok_or(gmsol_model::Error::Computation("calculating entry price"))?;
37
38 let pending_borrowing_fee_value = self.pending_borrowing_fee_value()?;
40
41 let pending_funding_fee = self.pending_funding_fees()?;
43 let pending_funding_fee_value = if self.is_collateral_token_long() {
44 pending_funding_fee
45 .amount()
46 .checked_mul(prices.long_token_price.min)
47 .ok_or(gmsol_model::Error::Computation(
48 "calculating pending funding fee value",
49 ))?
50 } else {
51 pending_funding_fee
52 .amount()
53 .checked_mul(prices.short_token_price.min)
54 .ok_or(gmsol_model::Error::Computation(
55 "calculating pending funding fee value",
56 ))?
57 };
58 let pending_claimable_funding_fee_value_in_long_token = pending_funding_fee
59 .claimable_long_token_amount()
60 .checked_mul(prices.long_token_price.min)
61 .ok_or(gmsol_model::Error::Computation(
62 "calculating pending claimable funding fee value in long token",
63 ))?;
64 let pending_claimable_funding_fee_value_in_short_token = pending_funding_fee
65 .claimable_short_token_amount()
66 .checked_mul(prices.short_token_price.min)
67 .ok_or(gmsol_model::Error::Computation(
68 "calculating pending claimable funding fee value in short token",
69 ))?;
70
71 let collateral_token_price = if self.is_collateral_token_long() {
73 prices.long_token_price
74 } else {
75 prices.short_token_price
76 };
77
78 let size_delta_usd = position_size_in_usd.to_opposite_signed()?;
80 let price_impact = self.position_price_impact(&size_delta_usd, true)?;
81
82 let mut price_impact_value = price_impact.value;
83 if price_impact_value.is_negative() {
84 self.market().cap_negative_position_price_impact(
85 &size_delta_usd,
86 true,
87 &mut price_impact_value,
88 )?;
89 } else {
90 price_impact_value = Zero::zero();
91 }
92
93 let total_position_fees = self.position_fees(
94 &collateral_token_price,
95 position_size_in_usd,
96 price_impact.balance_change,
97 false,
99 )?;
100
101 let close_order_fee_value = *total_position_fees.order_fees().fee_value();
102
103 let net_value = collateral_value
104 .to_signed()?
105 .checked_add(pending_pnl_value)
106 .ok_or(gmsol_model::Error::Computation("calculating net value"))?
107 .checked_sub(pending_borrowing_fee_value.to_signed()?)
108 .ok_or(gmsol_model::Error::Computation("calculating net value"))?
109 .checked_sub(pending_funding_fee_value.to_signed()?)
110 .ok_or(gmsol_model::Error::Computation("calculating net value"))?
111 .checked_sub(close_order_fee_value.to_signed()?)
112 .ok_or(gmsol_model::Error::Computation("calculating net value"))?
113 .max(Zero::zero());
114
115 let leverage = if !net_value.is_positive() {
117 None
118 } else {
119 Some(
120 gmsol_model::utils::div_to_factor::<_, { constants::MARKET_DECIMALS }>(
121 position_size_in_usd,
122 &net_value.unsigned_abs(),
123 true,
124 )
125 .ok_or(gmsol_model::Error::Computation("calculating leverage"))?,
126 )
127 };
128
129 let params = self.market().position_params()?;
131 let min_collateral_factor = params.min_collateral_factor();
132 let min_collateral_value = params.min_collateral_value();
133 let liquidation_collateral_usd = gmsol_model::utils::apply_factor::<
134 _,
135 { constants::MARKET_DECIMALS },
136 >(position_size_in_usd, min_collateral_factor)
137 .max(Some(*min_collateral_value))
138 .ok_or(gmsol_model::Error::Computation(
139 "calculating liquidation collateral usd",
140 ))?;
141
142 let liquidation_price = if position_size_in_tokens.is_zero() {
143 None
144 } else {
145 collateral_value
146 .checked_add_signed(price_impact_value)
147 .and_then(|a| a.checked_sub(pending_borrowing_fee_value))
148 .and_then(|a| a.checked_sub(pending_funding_fee_value))
149 .and_then(|a| a.checked_sub(close_order_fee_value))
150 .and_then(|remaining_collateral_usd| {
151 if self.is_long() {
152 liquidation_collateral_usd
153 .checked_add(*position_size_in_usd)?
154 .checked_sub(remaining_collateral_usd)?
155 .checked_div(*position_size_in_tokens)
156 } else {
157 remaining_collateral_usd
158 .checked_add(*position_size_in_usd)?
159 .checked_sub(liquidation_collateral_usd)?
160 .checked_div(*position_size_in_tokens)
161 }
162 })
163 };
164
165 Ok(PositionStatus {
166 entry_price,
167 collateral_value,
168 pending_pnl: pending_pnl_value,
169 pending_borrowing_fee_value,
170 pending_funding_fee_value,
171 pending_claimable_funding_fee_value_in_long_token,
172 pending_claimable_funding_fee_value_in_short_token,
173 close_order_fee_value,
174 net_value,
175 leverage,
176 liquidation_price,
177 })
178 }
179}