Skip to main content

ccxt_exchanges/bitget/
margin_impl.rs

1//! Margin trait implementation for Bitget.
2//!
3//! Implements the `Margin` trait from `ccxt-core` for Bitget, providing
4//! position management, leverage configuration, margin mode, and funding rate operations.
5
6use async_trait::async_trait;
7use ccxt_core::{
8    Result,
9    capability::ExchangeCapabilities,
10    traits::{Margin, PublicExchange},
11    types::{
12        FundingRate, FundingRateHistory, Position, Timeframe,
13        params::{LeverageParams, MarginMode},
14    },
15};
16
17use super::Bitget;
18
19// ============================================================================
20// PublicExchange Implementation
21// ============================================================================
22
23impl PublicExchange for Bitget {
24    fn id(&self) -> &'static str {
25        "bitget"
26    }
27
28    fn name(&self) -> &'static str {
29        "Bitget"
30    }
31
32    fn version(&self) -> &'static str {
33        "v2"
34    }
35
36    fn certified(&self) -> bool {
37        false
38    }
39
40    fn capabilities(&self) -> ExchangeCapabilities {
41        use ccxt_core::exchange::Capability;
42
43        ExchangeCapabilities::builder()
44            // Market Data
45            .market_data()
46            .without_capability(Capability::FetchCurrencies)
47            .without_capability(Capability::FetchStatus)
48            .without_capability(Capability::FetchTime)
49            // Trading
50            .trading()
51            .without_capability(Capability::CancelAllOrders)
52            .without_capability(Capability::EditOrder)
53            .without_capability(Capability::FetchOrders)
54            .without_capability(Capability::FetchCanceledOrders)
55            // Account
56            .capability(Capability::FetchBalance)
57            .capability(Capability::FetchMyTrades)
58            // Margin / Futures
59            .capability(Capability::FetchPositions)
60            .capability(Capability::SetLeverage)
61            .capability(Capability::SetMarginMode)
62            .capability(Capability::FetchFundingRate)
63            .capability(Capability::FetchFundingRates)
64            // WebSocket
65            .capability(Capability::Websocket)
66            .capability(Capability::WatchTicker)
67            .capability(Capability::WatchOrderBook)
68            .capability(Capability::WatchTrades)
69            .capability(Capability::WatchBalance)
70            .capability(Capability::WatchOrders)
71            .capability(Capability::WatchMyTrades)
72            .build()
73    }
74
75    fn timeframes(&self) -> Vec<Timeframe> {
76        vec![
77            Timeframe::M1,
78            Timeframe::M5,
79            Timeframe::M15,
80            Timeframe::M30,
81            Timeframe::H1,
82            Timeframe::H4,
83            Timeframe::H6,
84            Timeframe::H12,
85            Timeframe::D1,
86            Timeframe::D3,
87            Timeframe::W1,
88            Timeframe::Mon1,
89        ]
90    }
91
92    fn rate_limit(&self) -> u32 {
93        20
94    }
95
96    fn has_websocket(&self) -> bool {
97        true
98    }
99}
100
101// ============================================================================
102// Margin Trait Implementation
103// ============================================================================
104
105#[async_trait]
106impl Margin for Bitget {
107    async fn fetch_positions_for(&self, symbols: &[&str]) -> Result<Vec<Position>> {
108        self.fetch_positions_impl(symbols).await
109    }
110
111    async fn fetch_position(&self, symbol: &str) -> Result<Position> {
112        self.fetch_position_impl(symbol).await
113    }
114
115    async fn set_leverage_with_params(&self, params: LeverageParams) -> Result<()> {
116        let margin_mode = params.margin_mode.map(|m| match m {
117            MarginMode::Isolated => "isolated",
118            MarginMode::Cross => "cross",
119        });
120        self.set_leverage_impl(&params.symbol, params.leverage, margin_mode)
121            .await
122    }
123
124    async fn get_leverage(&self, symbol: &str) -> Result<u32> {
125        self.get_leverage_impl(symbol).await
126    }
127
128    async fn set_margin_mode(&self, symbol: &str, mode: MarginMode) -> Result<()> {
129        let mode_str = match mode {
130            MarginMode::Isolated => "isolated",
131            MarginMode::Cross => "cross",
132        };
133        self.set_margin_mode_impl(symbol, mode_str).await
134    }
135
136    async fn fetch_funding_rate(&self, symbol: &str) -> Result<FundingRate> {
137        self.fetch_funding_rate_impl(symbol).await
138    }
139
140    async fn fetch_funding_rates(&self, symbols: &[&str]) -> Result<Vec<FundingRate>> {
141        self.fetch_funding_rates_impl(symbols).await
142    }
143
144    async fn fetch_funding_rate_history(
145        &self,
146        symbol: &str,
147        since: Option<i64>,
148        limit: Option<u32>,
149    ) -> Result<Vec<FundingRateHistory>> {
150        self.fetch_funding_rate_history_impl(symbol, since, limit)
151            .await
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use ccxt_core::ExchangeConfig;
159
160    #[test]
161    fn test_bitget_public_exchange_impl() {
162        let bitget = Bitget::new(ExchangeConfig::default()).unwrap();
163        let pe: &dyn PublicExchange = &bitget;
164
165        assert_eq!(pe.id(), "bitget");
166        assert_eq!(pe.name(), "Bitget");
167        assert_eq!(pe.version(), "v2");
168        assert!(!pe.certified());
169        assert!(pe.has_websocket());
170        assert_eq!(pe.rate_limit(), 20);
171    }
172
173    #[test]
174    fn test_bitget_capabilities_include_margin() {
175        let bitget = Bitget::new(ExchangeConfig::default()).unwrap();
176        let caps = PublicExchange::capabilities(&bitget);
177
178        assert!(caps.has("fetchPositions"));
179        assert!(caps.has("setLeverage"));
180        assert!(caps.has("setMarginMode"));
181        assert!(caps.has("fetchFundingRate"));
182        assert!(caps.has("fetchFundingRates"));
183    }
184
185    #[test]
186    fn test_bitget_margin_trait_object_safety() {
187        let bitget = Bitget::new(ExchangeConfig::default()).unwrap();
188        let _margin: Box<dyn Margin> = Box::new(bitget);
189    }
190}