Skip to main content

ccxt_exchanges/okx/
margin_impl.rs

1//! Margin trait implementation for OKX.
2//!
3//! Implements the `Margin` trait from `ccxt-core` for OKX, 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::Okx;
18
19// ============================================================================
20// PublicExchange Implementation
21// ============================================================================
22
23impl PublicExchange for Okx {
24    fn id(&self) -> &'static str {
25        "okx"
26    }
27
28    fn name(&self) -> &'static str {
29        "OKX"
30    }
31
32    fn version(&self) -> &'static str {
33        "v5"
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::M3,
79            Timeframe::M5,
80            Timeframe::M15,
81            Timeframe::M30,
82            Timeframe::H1,
83            Timeframe::H2,
84            Timeframe::H4,
85            Timeframe::H6,
86            Timeframe::H12,
87            Timeframe::D1,
88            Timeframe::W1,
89            Timeframe::Mon1,
90        ]
91    }
92
93    fn rate_limit(&self) -> u32 {
94        20
95    }
96
97    fn has_websocket(&self) -> bool {
98        true
99    }
100}
101
102// ============================================================================
103// Margin Trait Implementation
104// ============================================================================
105
106#[async_trait]
107impl Margin for Okx {
108    async fn fetch_positions_for(&self, symbols: &[&str]) -> Result<Vec<Position>> {
109        self.fetch_positions_impl(symbols).await
110    }
111
112    async fn fetch_position(&self, symbol: &str) -> Result<Position> {
113        self.fetch_position_impl(symbol).await
114    }
115
116    async fn set_leverage_with_params(&self, params: LeverageParams) -> Result<()> {
117        let margin_mode = params.margin_mode.map(|m| match m {
118            MarginMode::Isolated => "isolated",
119            MarginMode::Cross => "cross",
120        });
121        self.set_leverage_impl(&params.symbol, params.leverage, margin_mode)
122            .await
123    }
124
125    async fn get_leverage(&self, symbol: &str) -> Result<u32> {
126        self.get_leverage_impl(symbol).await
127    }
128
129    async fn set_margin_mode(&self, symbol: &str, mode: MarginMode) -> Result<()> {
130        let mode_str = match mode {
131            MarginMode::Isolated => "isolated",
132            MarginMode::Cross => "cross",
133        };
134        self.set_margin_mode_impl(symbol, mode_str).await
135    }
136
137    async fn fetch_funding_rate(&self, symbol: &str) -> Result<FundingRate> {
138        self.fetch_funding_rate_impl(symbol).await
139    }
140
141    async fn fetch_funding_rates(&self, symbols: &[&str]) -> Result<Vec<FundingRate>> {
142        self.fetch_funding_rates_impl(symbols).await
143    }
144
145    async fn fetch_funding_rate_history(
146        &self,
147        symbol: &str,
148        since: Option<i64>,
149        limit: Option<u32>,
150    ) -> Result<Vec<FundingRateHistory>> {
151        self.fetch_funding_rate_history_impl(symbol, since, limit)
152            .await
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use ccxt_core::ExchangeConfig;
160
161    #[test]
162    fn test_okx_public_exchange_impl() {
163        let okx = Okx::new(ExchangeConfig::default()).unwrap();
164        let pe: &dyn PublicExchange = &okx;
165
166        assert_eq!(pe.id(), "okx");
167        assert_eq!(pe.name(), "OKX");
168        assert_eq!(pe.version(), "v5");
169        assert!(!pe.certified());
170        assert!(pe.has_websocket());
171        assert_eq!(pe.rate_limit(), 20);
172    }
173
174    #[test]
175    fn test_okx_capabilities_include_margin() {
176        let okx = Okx::new(ExchangeConfig::default()).unwrap();
177        let caps = PublicExchange::capabilities(&okx);
178
179        assert!(caps.has("fetchPositions"));
180        assert!(caps.has("setLeverage"));
181        assert!(caps.has("setMarginMode"));
182        assert!(caps.has("fetchFundingRate"));
183        assert!(caps.has("fetchFundingRates"));
184    }
185
186    #[test]
187    fn test_okx_margin_trait_object_safety() {
188        let okx = Okx::new(ExchangeConfig::default()).unwrap();
189        // Verify trait is object-safe by creating a trait object
190        let _margin: Box<dyn Margin> = Box::new(okx);
191    }
192}