ccxt_exchanges/binance/rest/futures/
funding.rs1use super::super::super::{Binance, parser};
4use ccxt_core::{
5 Error, ParseError, Result,
6 types::{FeeFundingRate, FeeFundingRateHistory, MarketType},
7};
8use serde_json::Value;
9use std::collections::{BTreeMap, HashMap};
10use tracing::warn;
11
12impl Binance {
13 pub async fn fetch_funding_rate(
15 &self,
16 symbol: &str,
17 params: Option<HashMap<String, String>>,
18 ) -> Result<FeeFundingRate> {
19 self.load_markets(false).await?;
20 let market = self.base().market(symbol).await?;
21
22 if market.market_type != MarketType::Futures && market.market_type != MarketType::Swap {
23 return Err(Error::invalid_request(
24 "fetch_funding_rate() supports futures and swap markets only".to_string(),
25 ));
26 }
27
28 let mut request_params = BTreeMap::new();
29 request_params.insert("symbol".to_string(), market.id.clone());
30
31 if let Some(p) = params {
32 for (key, value) in p {
33 request_params.insert(key, value);
34 }
35 }
36
37 let url = if market.linear.unwrap_or(true) {
38 format!("{}/premiumIndex", self.urls().fapi_public)
39 } else {
40 format!("{}/premiumIndex", self.urls().dapi_public)
41 };
42
43 let query = crate::binance::signed_request::build_query_string(&request_params);
44 let request_url = if query.is_empty() {
45 url
46 } else {
47 format!("{}?{}", url, query)
48 };
49
50 let response = self.public_get(&request_url, None).await?;
51
52 let data = if market.linear.unwrap_or(true) {
53 &response
54 } else {
55 response
56 .as_array()
57 .and_then(|arr| arr.first())
58 .ok_or_else(|| {
59 Error::from(ParseError::invalid_format(
60 "data",
61 "COIN-M funding rate response should be an array with at least one element",
62 ))
63 })?
64 };
65
66 parser::parse_funding_rate(data, Some(&market))
67 }
68
69 pub async fn fetch_funding_rates(
71 &self,
72 symbols: Option<Vec<String>>,
73 params: Option<BTreeMap<String, String>>,
74 ) -> Result<BTreeMap<String, FeeFundingRate>> {
75 self.load_markets(false).await?;
76
77 let mut request_params = BTreeMap::new();
78
79 if let Some(p) = params {
80 for (key, value) in p {
81 request_params.insert(key, value);
82 }
83 }
84
85 let url = format!("{}/premiumIndex", self.urls().fapi_public);
86
87 let mut request_url = url.clone();
88 if !request_params.is_empty() {
89 let query = crate::binance::signed_request::build_query_string(&request_params);
90 request_url = format!("{}?{}", url, query);
91 }
92
93 let response = self.public_get(&request_url, None).await?;
94
95 let mut rates = BTreeMap::new();
96
97 if let Some(rates_array) = response.as_array() {
98 for rate_data in rates_array {
99 if let Ok(symbol_id) = rate_data["symbol"]
100 .as_str()
101 .ok_or_else(|| Error::from(ParseError::missing_field("symbol")))
102 {
103 if let Ok(market) = self.base().market_by_id(symbol_id).await {
104 if let Some(ref syms) = symbols {
105 if !syms.contains(&market.symbol) {
106 continue;
107 }
108 }
109
110 if let Ok(rate) = parser::parse_funding_rate(rate_data, Some(&market)) {
111 rates.insert(market.symbol.clone(), rate);
112 }
113 }
114 }
115 }
116 } else if let Ok(symbol_id) = response["symbol"]
117 .as_str()
118 .ok_or_else(|| Error::from(ParseError::missing_field("symbol")))
119 {
120 if let Ok(market) = self.base().market_by_id(symbol_id).await {
121 if let Ok(rate) = parser::parse_funding_rate(&response, Some(&market)) {
122 rates.insert(market.symbol.clone(), rate);
123 }
124 }
125 }
126
127 Ok(rates)
128 }
129
130 pub async fn fetch_funding_rate_history(
132 &self,
133 symbol: &str,
134 since: Option<i64>,
135 limit: Option<u32>,
136 params: Option<HashMap<String, String>>,
137 ) -> Result<Vec<FeeFundingRateHistory>> {
138 self.load_markets(false).await?;
139 let market = self.base().market(symbol).await?;
140
141 if market.market_type != MarketType::Futures && market.market_type != MarketType::Swap {
142 return Err(Error::invalid_request(
143 "fetch_funding_rate_history() supports futures and swap markets only".to_string(),
144 ));
145 }
146
147 let mut request_params = BTreeMap::new();
148 request_params.insert("symbol".to_string(), market.id.clone());
149
150 if let Some(s) = since {
151 request_params.insert("startTime".to_string(), s.to_string());
152 }
153
154 if let Some(l) = limit {
155 request_params.insert("limit".to_string(), l.to_string());
156 }
157
158 if let Some(p) = params {
159 for (key, value) in p {
160 request_params.insert(key, value);
161 }
162 }
163
164 let url = if market.linear.unwrap_or(true) {
165 format!("{}/fundingRate", self.urls().fapi_public)
166 } else {
167 format!("{}/fundingRate", self.urls().dapi_public)
168 };
169
170 let query = crate::binance::signed_request::build_query_string(&request_params);
171 let request_url = if query.is_empty() {
172 url
173 } else {
174 format!("{}?{}", url, query)
175 };
176
177 let response = self.public_get(&request_url, None).await?;
178
179 let history_array = response.as_array().ok_or_else(|| {
180 Error::from(ParseError::invalid_format(
181 "data",
182 "Expected array of funding rate history",
183 ))
184 })?;
185
186 let mut history = Vec::new();
187 for history_data in history_array {
188 match parser::parse_funding_rate_history(history_data, Some(&market)) {
189 Ok(record) => history.push(record),
190 Err(e) => {
191 warn!(error = %e, "Failed to parse funding rate history");
192 }
193 }
194 }
195
196 Ok(history)
197 }
198
199 pub async fn fetch_funding_history(
201 &self,
202 symbol: Option<&str>,
203 since: Option<i64>,
204 limit: Option<u32>,
205 params: Option<HashMap<String, String>>,
206 ) -> Result<Value> {
207 self.load_markets(false).await?;
208
209 let url = format!("{}/income", self.urls().fapi_private);
210
211 let mut request = self.signed_request(url);
212
213 if let Some(sym) = symbol {
214 let market = self.base().market(sym).await?;
215 request = request.param("symbol", market.id.clone());
216 }
217
218 request = request
219 .optional_param("startTime", since)
220 .optional_param("limit", limit);
221
222 let json_params = params.map(|p| {
224 serde_json::Value::Object(
225 p.into_iter()
226 .map(|(k, v)| (k, serde_json::Value::String(v)))
227 .collect(),
228 )
229 });
230
231 let data = request.merge_json_params(json_params).execute().await?;
232
233 Ok(data)
234 }
235}