Skip to main content

ccxt_exchanges/binance/rest/futures/
positions.rs

1//! Position management methods for Binance futures.
2
3use super::super::super::{Binance, parser};
4use ccxt_core::{Error, ParseError, Result, types::Position};
5use serde_json::Value;
6use std::sync::Arc;
7use tracing::warn;
8
9impl Binance {
10    /// Fetch a single position for a trading pair.
11    pub async fn fetch_position(&self, symbol: &str, params: Option<Value>) -> Result<Position> {
12        let market = self.base().market(symbol).await?;
13
14        let url = if market.linear.unwrap_or(true) {
15            format!("{}/positionRisk", self.urls().fapi_private)
16        } else {
17            format!("{}/positionRisk", self.urls().dapi_private)
18        };
19
20        let data = self
21            .signed_request(url)
22            .param("symbol", &market.id)
23            .merge_json_params(params)
24            .execute()
25            .await?;
26
27        let positions_array = data.as_array().ok_or_else(|| {
28            Error::from(ParseError::invalid_format(
29                "data",
30                "Expected array of positions",
31            ))
32        })?;
33
34        for position_data in positions_array {
35            if let Some(pos_symbol) = position_data["symbol"].as_str() {
36                if pos_symbol == market.id {
37                    return parser::parse_position(position_data, Some(&market));
38                }
39            }
40        }
41
42        Err(Error::from(ParseError::missing_field_owned(format!(
43            "Position not found for symbol: {}",
44            symbol
45        ))))
46    }
47
48    /// Fetch all positions.
49    pub async fn fetch_positions(
50        &self,
51        symbols: Option<Vec<String>>,
52        params: Option<Value>,
53    ) -> Result<Vec<Position>> {
54        let use_coin_m = params
55            .as_ref()
56            .and_then(|p| p.get("type"))
57            .and_then(serde_json::Value::as_str)
58            .is_some_and(|t| t == "delivery" || t == "coin_m");
59
60        let url = if use_coin_m {
61            format!("{}/positionRisk", self.urls().dapi_private)
62        } else {
63            format!("{}/positionRisk", self.urls().fapi_private)
64        };
65
66        let data = self
67            .signed_request(url)
68            .merge_json_params(params)
69            .execute()
70            .await?;
71
72        let positions_array = data.as_array().ok_or_else(|| {
73            Error::from(ParseError::invalid_format(
74                "data",
75                "Expected array of positions",
76            ))
77        })?;
78
79        let cache = self.base().market_cache.read().await;
80        let markets_snapshot: std::collections::HashMap<String, Arc<ccxt_core::types::Market>> =
81            cache
82                .iter_markets()
83                .map(|(_, m)| (m.id.clone(), m))
84                .collect();
85        drop(cache);
86
87        let mut positions = Vec::new();
88        for position_data in positions_array {
89            if let Some(binance_symbol) = position_data["symbol"].as_str() {
90                if let Some(market) = markets_snapshot.get(binance_symbol) {
91                    match parser::parse_position(position_data, Some(market)) {
92                        Ok(position) => {
93                            if position.contracts.unwrap_or(0.0) > 0.0 {
94                                if let Some(ref syms) = symbols {
95                                    if syms.contains(&position.symbol) {
96                                        positions.push(position);
97                                    }
98                                } else {
99                                    positions.push(position);
100                                }
101                            }
102                        }
103                        Err(e) => {
104                            warn!(
105                                error = %e,
106                                symbol = %binance_symbol,
107                                "Failed to parse position"
108                            );
109                        }
110                    }
111                }
112            }
113        }
114
115        Ok(positions)
116    }
117
118    /// Fetch position risk information.
119    pub async fn fetch_positions_risk(
120        &self,
121        symbols: Option<Vec<String>>,
122        params: Option<Value>,
123    ) -> Result<Vec<Position>> {
124        self.fetch_positions(symbols, params).await
125    }
126
127    /// Fetch position risk information (raw JSON).
128    pub async fn fetch_position_risk(
129        &self,
130        symbol: Option<&str>,
131        params: Option<Value>,
132    ) -> Result<Value> {
133        let market_id = if let Some(sym) = symbol {
134            let market = self.base().market(sym).await?;
135            Some(market.id.clone())
136        } else {
137            None
138        };
139
140        let url = format!("{}/positionRisk", self.urls().fapi_private);
141
142        self.signed_request(url)
143            .optional_param("symbol", market_id)
144            .merge_json_params(params)
145            .execute()
146            .await
147    }
148}