Skip to main content

bybit_api/api/
position.rs

1//! Position API endpoints.
2
3use crate::client::BybitClient;
4use crate::error::Result;
5use crate::models::position::*;
6use crate::models::Category;
7use crate::models::*;
8
9impl BybitClient {
10    /// Get position list.
11    ///
12    /// # Arguments
13    /// * `category` - Product category
14    /// * `symbol` - Optional symbol filter
15    /// * `settle_coin` - Optional settle coin filter (e.g., "USDT")
16    pub async fn get_positions(
17        &self,
18        category: Category,
19        symbol: Option<&str>,
20        settle_coin: Option<&str>,
21    ) -> Result<PositionList> {
22        let cat_str = category.to_string();
23        let mut params = vec![("category", cat_str.as_str())];
24
25        if let Some(s) = symbol {
26            params.push(("symbol", s));
27        }
28        if let Some(sc) = settle_coin {
29            params.push(("settleCoin", sc));
30        }
31
32        self.get("/v5/position/list", &params).await
33    }
34
35    /// Set leverage.
36    ///
37    /// # Arguments
38    /// * `category` - Product category
39    /// * `symbol` - Symbol name
40    /// * `buy_leverage` - Buy leverage
41    /// * `sell_leverage` - Sell leverage
42    // FIXME(typed-signature): falls back to `serde_json::Value` because the
43    // OpenAPI spec referenced a response/request type that gen-sdk-rust could
44    // not auto-resolve. Replace with a typed struct in a follow-up PR.
45    pub async fn set_leverage(
46        &self,
47        category: Category,
48        symbol: &str,
49        buy_leverage: &str,
50        sell_leverage: &str,
51    ) -> Result<serde_json::Value> {
52        let params = SetLeverageParams {
53            category,
54            symbol: symbol.to_string(),
55            buy_leverage: buy_leverage.to_string(),
56            sell_leverage: sell_leverage.to_string(),
57        };
58
59        self.post("/v5/position/set-leverage", &params).await
60    }
61
62    /// Set trading stop (TP/SL).
63    ///
64    /// # Arguments
65    /// * `params` - Trading stop parameters
66    // FIXME(typed-signature): falls back to `serde_json::Value` because the
67    // OpenAPI spec referenced a response/request type that gen-sdk-rust could
68    // not auto-resolve. Replace with a typed struct in a follow-up PR.
69    pub async fn set_trading_stop(&self, params: TradingStopParams) -> Result<serde_json::Value> {
70        self.post("/v5/position/trading-stop", &params).await
71    }
72
73    /// Switch position mode.
74    ///
75    /// # Arguments
76    /// * `category` - Product category
77    /// * `mode` - Position mode (0=merged, 3=both sides)
78    // FIXME(typed-signature): falls back to `serde_json::Value` because the
79    // OpenAPI spec referenced a response/request type that gen-sdk-rust could
80    // not auto-resolve. Replace with a typed struct in a follow-up PR.
81    pub async fn switch_position_mode(
82        &self,
83        category: Category,
84        mode: PositionMode,
85    ) -> Result<serde_json::Value> {
86        let params = SwitchPositionModeParams {
87            category,
88            symbol: None,
89            coin: None,
90            mode: mode as i32,
91        };
92
93        self.post("/v5/position/switch-mode", &params).await
94    }
95
96    /// Set risk limit.
97    ///
98    /// # Arguments
99    /// * `category` - Product category
100    /// * `symbol` - Symbol name
101    /// * `risk_id` - Risk limit ID
102    // FIXME(typed-signature): falls back to `serde_json::Value` because the
103    // OpenAPI spec referenced a response/request type that gen-sdk-rust could
104    // not auto-resolve. Replace with a typed struct in a follow-up PR.
105    pub async fn set_risk_limit(
106        &self,
107        category: Category,
108        symbol: &str,
109        risk_id: i32,
110    ) -> Result<serde_json::Value> {
111        let params = SetRiskLimitParams {
112            category,
113            symbol: symbol.to_string(),
114            risk_id,
115            position_idx: None,
116        };
117
118        self.post("/v5/position/set-risk-limit", &params).await
119    }
120
121    /// Add or reduce margin.
122    ///
123    /// # Arguments
124    /// * `category` - Product category
125    /// * `symbol` - Symbol name
126    /// * `margin` - Margin amount (positive to add, negative to reduce)
127    // FIXME(typed-signature): falls back to `serde_json::Value` because the
128    // OpenAPI spec referenced a response/request type that gen-sdk-rust could
129    // not auto-resolve. Replace with a typed struct in a follow-up PR.
130    pub async fn add_margin(
131        &self,
132        category: Category,
133        symbol: &str,
134        margin: &str,
135    ) -> Result<serde_json::Value> {
136        let params = AddMarginParams {
137            category,
138            symbol: symbol.to_string(),
139            margin: margin.to_string(),
140            position_idx: None,
141        };
142
143        self.post("/v5/position/add-margin", &params).await
144    }
145
146    /// Get closed PnL history.
147    ///
148    /// # Arguments
149    /// * `category` - Product category
150    /// * `symbol` - Optional symbol filter
151    /// * `limit` - Optional limit (default 20)
152    pub async fn get_closed_pnl(
153        &self,
154        category: Category,
155        symbol: Option<&str>,
156        limit: Option<u32>,
157    ) -> Result<ClosedPnlList> {
158        let cat_str = category.to_string();
159        let limit_str = limit.unwrap_or(20).to_string();
160        let mut params = vec![
161            ("category", cat_str.as_str()),
162            ("limit", limit_str.as_str()),
163        ];
164
165        if let Some(s) = symbol {
166            params.push(("symbol", s));
167        }
168
169        self.get("/v5/position/closed-pnl", &params).await
170    }
171
172    /// Get execution list (trade history).
173    ///
174    /// # Arguments
175    /// * `category` - Product category
176    /// * `symbol` - Optional symbol filter
177    /// * `limit` - Optional limit (default 50)
178    pub async fn get_executions(
179        &self,
180        category: Category,
181        symbol: Option<&str>,
182        limit: Option<u32>,
183    ) -> Result<ExecutionList> {
184        let cat_str = category.to_string();
185        let limit_str = limit.unwrap_or(50).to_string();
186        let mut params = vec![
187            ("category", cat_str.as_str()),
188            ("limit", limit_str.as_str()),
189        ];
190
191        if let Some(s) = symbol {
192            params.push(("symbol", s));
193        }
194
195        self.get("/v5/execution/list", &params).await
196    }
197
198    pub async fn confirm_new_risk_limit(
199        &self,
200        params: ConfirmNewRiskLimitParams,
201    ) -> Result<ConfirmNewRiskLimitResponse> {
202        self.post("/v5/position/confirm-pending-mmr", &params).await
203    }
204
205    pub async fn get_close_position(
206        &self,
207        category: Category,
208        symbol: Option<&str>,
209        start_time: Option<i64>,
210        end_time: Option<i64>,
211        limit: Option<i64>,
212        cursor: Option<&str>,
213    ) -> Result<GetClosePositionResponse> {
214        let cat_str = category.to_string();
215        let start_time_str = start_time.map(|v| v.to_string());
216        let end_time_str = end_time.map(|v| v.to_string());
217        let limit_str = limit.map(|v| v.to_string());
218        let mut params = vec![("category", cat_str.as_str())];
219        if let Some(s) = symbol {
220            params.push(("symbol", s));
221        }
222        if let Some(ref st) = start_time_str {
223            params.push(("startTime", st.as_str()));
224        }
225        if let Some(ref et) = end_time_str {
226            params.push(("endTime", et.as_str()));
227        }
228        if let Some(ref l) = limit_str {
229            params.push(("limit", l.as_str()));
230        }
231        if let Some(c) = cursor {
232            params.push(("cursor", c));
233        }
234        self.get("/v5/position/get-closed-positions", &params).await
235    }
236
237    #[allow(clippy::too_many_arguments)] // TODO(api-ergonomics): convert positional args to a typed `*Params` struct
238    pub async fn get_move_position_history(
239        &self,
240        category: Option<Category>,
241        symbol: Option<&str>,
242        start_time: Option<i64>,
243        end_time: Option<i64>,
244        status: Option<&str>,
245        block_trade_id: Option<&str>,
246        limit: Option<&str>,
247        cursor: Option<&str>,
248    ) -> Result<GetMovePositionHistoryResponse> {
249        let cat_str = category.map(|c| c.to_string());
250        let start_time_str = start_time.map(|v| v.to_string());
251        let end_time_str = end_time.map(|v| v.to_string());
252        let mut params: Vec<(&str, &str)> = Vec::new();
253        if let Some(ref c) = cat_str {
254            params.push(("category", c.as_str()));
255        }
256        if let Some(s) = symbol {
257            params.push(("symbol", s));
258        }
259        if let Some(ref st) = start_time_str {
260            params.push(("startTime", st.as_str()));
261        }
262        if let Some(ref et) = end_time_str {
263            params.push(("endTime", et.as_str()));
264        }
265        if let Some(s) = status {
266            params.push(("status", s));
267        }
268        if let Some(b) = block_trade_id {
269            params.push(("blockTradeId", b));
270        }
271        if let Some(l) = limit {
272            params.push(("limit", l));
273        }
274        if let Some(c) = cursor {
275            params.push(("cursor", c));
276        }
277        self.get("/v5/position/move-history", &params).await
278    }
279
280    pub async fn move_position(&self, params: MovePositionParams) -> Result<MovePositionResponse> {
281        self.post("/v5/position/move-positions", &params).await
282    }
283
284    pub async fn set_auto_add_margin(
285        &self,
286        params: SetAutoAddMarginParams,
287    ) -> Result<SetAutoAddMarginResponse> {
288        self.post("/v5/position/set-auto-add-margin", &params).await
289    }
290}