gmsol_programs/model/
position.rs

1use std::sync::Arc;
2
3use gmsol_model::action::{decrease_position::DecreasePositionSwapType, swap::SwapReport};
4
5use crate::{
6    constants,
7    gmsol_store::{accounts::Position, types::PositionState},
8};
9
10use super::MarketModel;
11
12/// Position Model.
13#[derive(Debug, Clone)]
14pub struct PositionModel {
15    market: MarketModel,
16    position: Arc<Position>,
17    is_long: bool,
18    is_collateral_token_long: bool,
19    swap_history: Vec<Arc<SwapReport<u128, i128>>>,
20}
21
22#[repr(u8)]
23pub(super) enum PositionKind {
24    #[allow(dead_code)]
25    Uninitialized,
26    Long,
27    Short,
28}
29
30impl Position {
31    /// Return whether the side of the position is long.
32    pub fn try_is_long(&self) -> gmsol_model::Result<bool> {
33        if self.kind == PositionKind::Long as u8 {
34            Ok(true)
35        } else if self.kind == PositionKind::Short as u8 {
36            Ok(false)
37        } else {
38            Err(gmsol_model::Error::InvalidPosition(
39                "uninitialized position",
40            ))
41        }
42    }
43}
44
45impl PositionModel {
46    /// Create from [`MarketModel`] and [`Position`].
47    pub fn new(market: MarketModel, position: Arc<Position>) -> gmsol_model::Result<Self> {
48        let is_long = position.try_is_long()?;
49        let is_collateral_token_long = market.meta.token_side(&position.collateral_token)?;
50        Ok(Self {
51            market,
52            position,
53            is_long,
54            is_collateral_token_long,
55            swap_history: vec![],
56        })
57    }
58
59    fn make_position_mut(&mut self) -> &mut Position {
60        Arc::make_mut(&mut self.position)
61    }
62
63    /// Get position.
64    pub fn position(&self) -> &Position {
65        &self.position
66    }
67
68    /// Returns a reference to the position wrapped in an `Arc`.
69    pub fn position_arc(&self) -> &Arc<Position> {
70        &self.position
71    }
72
73    /// Returns the swap history.
74    pub fn swap_history(&self) -> &[Arc<SwapReport<u128, i128>>] {
75        &self.swap_history
76    }
77
78    /// Clear the swap history.
79    pub fn clear(&mut self) {
80        self.swap_history.clear();
81    }
82
83    /// Updates the current state with the given [`PositionState`]
84    /// if its `trade_id` is newer.
85    ///
86    /// Skips the trade ID check if `force_update` is `true`.
87    ///
88    /// Returns `true` if an update was made.
89    pub fn update(&mut self, new_state: &PositionState, force_update: bool) -> bool {
90        let position = &mut self.position;
91
92        if !force_update && new_state.trade_id <= position.state.trade_id {
93            return false;
94        }
95        Arc::make_mut(position).state = *new_state;
96
97        true
98    }
99
100    /// Get the market model.
101    pub fn market_model(&self) -> &MarketModel {
102        &self.market
103    }
104
105    /// Set market model to the given.
106    pub fn set_market_model(&mut self, market: &MarketModel) {
107        self.market = market.clone();
108    }
109}
110
111impl gmsol_model::PositionState<{ constants::MARKET_DECIMALS }> for PositionState {
112    type Num = u128;
113
114    type Signed = i128;
115
116    fn collateral_amount(&self) -> &Self::Num {
117        &self.collateral_amount
118    }
119
120    fn size_in_usd(&self) -> &Self::Num {
121        &self.size_in_usd
122    }
123
124    fn size_in_tokens(&self) -> &Self::Num {
125        &self.size_in_tokens
126    }
127
128    fn borrowing_factor(&self) -> &Self::Num {
129        &self.borrowing_factor
130    }
131
132    fn funding_fee_amount_per_size(&self) -> &Self::Num {
133        &self.funding_fee_amount_per_size
134    }
135
136    fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num {
137        if is_long_collateral {
138            &self.long_token_claimable_funding_amount_per_size
139        } else {
140            &self.short_token_claimable_funding_amount_per_size
141        }
142    }
143}
144
145impl gmsol_model::PositionState<{ constants::MARKET_DECIMALS }> for PositionModel {
146    type Num = u128;
147
148    type Signed = i128;
149
150    fn collateral_amount(&self) -> &Self::Num {
151        self.position.state.collateral_amount()
152    }
153
154    fn size_in_usd(&self) -> &Self::Num {
155        self.position.state.size_in_usd()
156    }
157
158    fn size_in_tokens(&self) -> &Self::Num {
159        self.position.state.size_in_tokens()
160    }
161
162    fn borrowing_factor(&self) -> &Self::Num {
163        self.position.state.borrowing_factor()
164    }
165
166    fn funding_fee_amount_per_size(&self) -> &Self::Num {
167        self.position.state.funding_fee_amount_per_size()
168    }
169
170    fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num {
171        self.position
172            .state
173            .claimable_funding_fee_amount_per_size(is_long_collateral)
174    }
175}
176
177impl gmsol_model::PositionStateMut<{ constants::MARKET_DECIMALS }> for PositionModel {
178    fn collateral_amount_mut(&mut self) -> &mut Self::Num {
179        &mut self.make_position_mut().state.collateral_amount
180    }
181
182    fn size_in_usd_mut(&mut self) -> &mut Self::Num {
183        &mut self.make_position_mut().state.size_in_usd
184    }
185
186    fn size_in_tokens_mut(&mut self) -> &mut Self::Num {
187        &mut self.make_position_mut().state.size_in_tokens
188    }
189
190    fn borrowing_factor_mut(&mut self) -> &mut Self::Num {
191        &mut self.make_position_mut().state.borrowing_factor
192    }
193
194    fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num {
195        &mut self.make_position_mut().state.funding_fee_amount_per_size
196    }
197
198    fn claimable_funding_fee_amount_per_size_mut(
199        &mut self,
200        is_long_collateral: bool,
201    ) -> &mut Self::Num {
202        if is_long_collateral {
203            &mut self
204                .make_position_mut()
205                .state
206                .long_token_claimable_funding_amount_per_size
207        } else {
208            &mut self
209                .make_position_mut()
210                .state
211                .short_token_claimable_funding_amount_per_size
212        }
213    }
214}
215
216impl gmsol_model::Position<{ constants::MARKET_DECIMALS }> for PositionModel {
217    type Market = MarketModel;
218
219    fn market(&self) -> &Self::Market {
220        &self.market
221    }
222
223    fn is_long(&self) -> bool {
224        self.is_long
225    }
226
227    fn is_collateral_token_long(&self) -> bool {
228        self.is_collateral_token_long
229    }
230
231    fn are_pnl_and_collateral_tokens_the_same(&self) -> bool {
232        self.is_long == self.is_collateral_token_long || self.market.is_pure()
233    }
234
235    fn on_validate(&self) -> gmsol_model::Result<()> {
236        Ok(())
237    }
238}
239
240impl gmsol_model::PositionMut<{ constants::MARKET_DECIMALS }> for PositionModel {
241    fn market_mut(&mut self) -> &mut Self::Market {
242        &mut self.market
243    }
244
245    fn on_increased(&mut self) -> gmsol_model::Result<()> {
246        Ok(())
247    }
248
249    fn on_decreased(&mut self) -> gmsol_model::Result<()> {
250        Ok(())
251    }
252
253    fn on_swapped(
254        &mut self,
255        _ty: DecreasePositionSwapType,
256        report: &SwapReport<Self::Num, <Self::Num as gmsol_model::num::Unsigned>::Signed>,
257    ) -> gmsol_model::Result<()> {
258        self.swap_history.push(Arc::new(report.clone()));
259        Ok(())
260    }
261
262    fn on_swap_error(
263        &mut self,
264        _ty: DecreasePositionSwapType,
265        _error: gmsol_model::Error,
266    ) -> gmsol_model::Result<()> {
267        Ok(())
268    }
269}