ccxt_exchanges/binance/rest/futures/
margin.rs1use super::super::super::{Binance, signed_request::HttpMethod};
4use ccxt_core::{Error, ParseError, Result, types::MarketType};
5use rust_decimal::Decimal;
6use serde_json::Value;
7use std::collections::{BTreeMap, HashMap};
8
9impl Binance {
10 pub async fn set_margin_mode(
12 &self,
13 symbol: &str,
14 margin_mode: &str,
15 params: Option<HashMap<String, String>>,
16 ) -> Result<HashMap<String, Value>> {
17 let margin_type = match margin_mode.to_uppercase().as_str() {
18 "ISOLATED" | "ISOLATED_MARGIN" => "ISOLATED",
19 "CROSS" | "CROSSED" | "CROSS_MARGIN" => "CROSSED",
20 _ => {
21 return Err(Error::invalid_request(format!(
22 "Invalid margin mode: {}. Must be 'isolated' or 'cross'",
23 margin_mode
24 )));
25 }
26 };
27
28 self.load_markets(false).await?;
29 let market = self.base().market(symbol).await?;
30
31 if market.market_type != MarketType::Futures && market.market_type != MarketType::Swap {
32 return Err(Error::invalid_request(
33 "set_margin_mode() supports futures and swap markets only".to_string(),
34 ));
35 }
36
37 let url = if market.linear.unwrap_or(true) {
38 format!("{}/marginType", self.urls().fapi_private)
39 } else if market.inverse.unwrap_or(false) {
40 format!("{}/marginType", self.urls().dapi_private)
41 } else {
42 return Err(Error::invalid_request(
43 "Unknown futures market type".to_string(),
44 ));
45 };
46
47 let mut builder = self
48 .signed_request(url)
49 .method(HttpMethod::Post)
50 .param("symbol", &market.id)
51 .param("marginType", margin_type);
52
53 if let Some(p) = params {
54 let params_map: BTreeMap<String, String> = p.into_iter().collect();
55 builder = builder.params(params_map);
56 }
57
58 let response = builder.execute().await?;
59
60 let result: HashMap<String, Value> = serde_json::from_value(response).map_err(|e| {
61 Error::from(ParseError::invalid_format(
62 "data",
63 format!("Failed to parse response: {}", e),
64 ))
65 })?;
66
67 Ok(result)
68 }
69
70 pub async fn set_position_mode(&self, dual_side: bool, params: Option<Value>) -> Result<Value> {
72 let use_dapi = params
73 .as_ref()
74 .and_then(|p| p.get("type"))
75 .and_then(serde_json::Value::as_str)
76 .is_some_and(|t| t == "delivery" || t == "coin_m");
77
78 let url = if use_dapi {
79 format!("{}/positionSide/dual", self.urls().dapi_private)
80 } else {
81 format!("{}/positionSide/dual", self.urls().fapi_private)
82 };
83
84 let builder = self
85 .signed_request(url)
86 .method(HttpMethod::Post)
87 .param("dualSidePosition", dual_side)
88 .merge_json_params(params);
89
90 let data = builder.execute().await?;
91 Ok(data)
92 }
93
94 pub async fn fetch_position_mode(&self, params: Option<Value>) -> Result<bool> {
96 let use_dapi = params
97 .as_ref()
98 .and_then(|p| p.get("type"))
99 .and_then(serde_json::Value::as_str)
100 .is_some_and(|t| t == "delivery" || t == "coin_m");
101
102 let url = if use_dapi {
103 format!("{}/positionSide/dual", self.urls().dapi_private)
104 } else {
105 format!("{}/positionSide/dual", self.urls().fapi_private)
106 };
107
108 let builder = self.signed_request(url).merge_json_params(params);
109
110 let data = builder.execute().await?;
111
112 if let Some(dual_side) = data.get("dualSidePosition") {
113 if let Some(value) = dual_side.as_bool() {
114 return Ok(value);
115 }
116 if let Some(value_str) = dual_side.as_str() {
117 return Ok(value_str.to_lowercase() == "true");
118 }
119 }
120
121 Err(Error::from(ParseError::invalid_format(
122 "data",
123 "Failed to parse position mode response",
124 )))
125 }
126
127 pub async fn modify_isolated_position_margin(
129 &self,
130 symbol: &str,
131 amount: Decimal,
132 params: Option<Value>,
133 ) -> Result<Value> {
134 let market = self.base().market(symbol).await?;
135
136 let url = if market.linear.unwrap_or(true) {
137 format!("{}/positionMargin", self.urls().fapi_private)
138 } else {
139 format!("{}/positionMargin", self.urls().dapi_private)
140 };
141
142 let margin_type = if amount > Decimal::ZERO { "1" } else { "2" };
143
144 let builder = self
145 .signed_request(url)
146 .method(HttpMethod::Post)
147 .param("symbol", &market.id)
148 .param("amount", amount.abs())
149 .param("type", margin_type)
150 .merge_json_params(params);
151
152 let data = builder.execute().await?;
153 Ok(data)
154 }
155}