ccxt_core/capability/
exchange_caps.rs

1//! Exchange capability constants and high-level API
2
3use std::fmt;
4
5use super::{Capabilities, Capability};
6
7/// Exchange capabilities configuration
8///
9/// This struct provides a high-level API for working with exchange capabilities,
10/// maintaining backward compatibility with the original boolean-field design
11/// while using efficient bitflags internally.
12///
13/// # Example
14///
15/// ```rust
16/// use ccxt_core::capability::ExchangeCapabilities;
17///
18/// let caps = ExchangeCapabilities::public_only();
19/// assert!(caps.has("fetchTicker"));
20/// assert!(!caps.has("createOrder"));
21///
22/// let caps = ExchangeCapabilities::all();
23/// assert!(caps.fetch_ticker());
24/// assert!(caps.create_order());
25/// ```
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
27pub struct ExchangeCapabilities {
28    inner: Capabilities,
29}
30
31impl ExchangeCapabilities {
32    /// Create with no capabilities enabled
33    pub const fn none() -> Self {
34        Self {
35            inner: Capabilities::empty(),
36        }
37    }
38
39    /// Create capabilities with all features enabled
40    pub const fn all() -> Self {
41        Self {
42            inner: Capabilities::ALL,
43        }
44    }
45
46    /// Create capabilities for public API only (no authentication required)
47    pub const fn public_only() -> Self {
48        Self {
49            inner: Capabilities::PUBLIC_ONLY,
50        }
51    }
52
53    /// Create from raw Capabilities bitflags
54    pub const fn from_capabilities(caps: Capabilities) -> Self {
55        Self { inner: caps }
56    }
57
58    /// Get the underlying Capabilities bitflags
59    pub const fn as_capabilities(&self) -> Capabilities {
60        self.inner
61    }
62
63    /// Check if a capability is supported by name
64    pub fn has(&self, capability: &str) -> bool {
65        self.inner.has(capability)
66    }
67
68    /// Get a list of all supported capability names
69    pub fn supported_capabilities(&self) -> Vec<&'static str> {
70        self.inner.supported_capabilities()
71    }
72
73    /// Count the number of enabled capabilities
74    #[inline]
75    pub fn count(&self) -> u32 {
76        self.inner.count()
77    }
78
79    // ==================== Market Data Accessors ====================
80
81    #[inline]
82    pub const fn fetch_markets(&self) -> bool {
83        self.inner.contains(Capabilities::FETCH_MARKETS)
84    }
85
86    #[inline]
87    pub const fn fetch_currencies(&self) -> bool {
88        self.inner.contains(Capabilities::FETCH_CURRENCIES)
89    }
90
91    #[inline]
92    pub const fn fetch_ticker(&self) -> bool {
93        self.inner.contains(Capabilities::FETCH_TICKER)
94    }
95
96    #[inline]
97    pub const fn fetch_tickers(&self) -> bool {
98        self.inner.contains(Capabilities::FETCH_TICKERS)
99    }
100
101    #[inline]
102    pub const fn fetch_order_book(&self) -> bool {
103        self.inner.contains(Capabilities::FETCH_ORDER_BOOK)
104    }
105
106    #[inline]
107    pub const fn fetch_trades(&self) -> bool {
108        self.inner.contains(Capabilities::FETCH_TRADES)
109    }
110
111    #[inline]
112    pub const fn fetch_ohlcv(&self) -> bool {
113        self.inner.contains(Capabilities::FETCH_OHLCV)
114    }
115
116    #[inline]
117    pub const fn fetch_status(&self) -> bool {
118        self.inner.contains(Capabilities::FETCH_STATUS)
119    }
120
121    #[inline]
122    pub const fn fetch_time(&self) -> bool {
123        self.inner.contains(Capabilities::FETCH_TIME)
124    }
125
126    // ==================== Trading Accessors ====================
127
128    #[inline]
129    pub const fn create_order(&self) -> bool {
130        self.inner.contains(Capabilities::CREATE_ORDER)
131    }
132
133    #[inline]
134    pub const fn create_market_order(&self) -> bool {
135        self.inner.contains(Capabilities::CREATE_MARKET_ORDER)
136    }
137
138    #[inline]
139    pub const fn create_limit_order(&self) -> bool {
140        self.inner.contains(Capabilities::CREATE_LIMIT_ORDER)
141    }
142
143    #[inline]
144    pub const fn cancel_order(&self) -> bool {
145        self.inner.contains(Capabilities::CANCEL_ORDER)
146    }
147
148    #[inline]
149    pub const fn cancel_all_orders(&self) -> bool {
150        self.inner.contains(Capabilities::CANCEL_ALL_ORDERS)
151    }
152
153    #[inline]
154    pub const fn edit_order(&self) -> bool {
155        self.inner.contains(Capabilities::EDIT_ORDER)
156    }
157
158    #[inline]
159    pub const fn fetch_order(&self) -> bool {
160        self.inner.contains(Capabilities::FETCH_ORDER)
161    }
162
163    #[inline]
164    pub const fn fetch_orders(&self) -> bool {
165        self.inner.contains(Capabilities::FETCH_ORDERS)
166    }
167
168    #[inline]
169    pub const fn fetch_open_orders(&self) -> bool {
170        self.inner.contains(Capabilities::FETCH_OPEN_ORDERS)
171    }
172
173    #[inline]
174    pub const fn fetch_closed_orders(&self) -> bool {
175        self.inner.contains(Capabilities::FETCH_CLOSED_ORDERS)
176    }
177
178    #[inline]
179    pub const fn fetch_canceled_orders(&self) -> bool {
180        self.inner.contains(Capabilities::FETCH_CANCELED_ORDERS)
181    }
182
183    // ==================== Account Accessors ====================
184
185    #[inline]
186    pub const fn fetch_balance(&self) -> bool {
187        self.inner.contains(Capabilities::FETCH_BALANCE)
188    }
189
190    #[inline]
191    pub const fn fetch_my_trades(&self) -> bool {
192        self.inner.contains(Capabilities::FETCH_MY_TRADES)
193    }
194
195    #[inline]
196    pub const fn fetch_deposits(&self) -> bool {
197        self.inner.contains(Capabilities::FETCH_DEPOSITS)
198    }
199
200    #[inline]
201    pub const fn fetch_withdrawals(&self) -> bool {
202        self.inner.contains(Capabilities::FETCH_WITHDRAWALS)
203    }
204
205    #[inline]
206    pub const fn fetch_transactions(&self) -> bool {
207        self.inner.contains(Capabilities::FETCH_TRANSACTIONS)
208    }
209
210    #[inline]
211    pub const fn fetch_ledger(&self) -> bool {
212        self.inner.contains(Capabilities::FETCH_LEDGER)
213    }
214
215    // ==================== Funding Accessors ====================
216
217    #[inline]
218    pub const fn fetch_deposit_address(&self) -> bool {
219        self.inner.contains(Capabilities::FETCH_DEPOSIT_ADDRESS)
220    }
221
222    #[inline]
223    pub const fn create_deposit_address(&self) -> bool {
224        self.inner.contains(Capabilities::CREATE_DEPOSIT_ADDRESS)
225    }
226
227    #[inline]
228    pub const fn withdraw(&self) -> bool {
229        self.inner.contains(Capabilities::WITHDRAW)
230    }
231
232    #[inline]
233    pub const fn transfer(&self) -> bool {
234        self.inner.contains(Capabilities::TRANSFER)
235    }
236
237    // ==================== Margin Trading Accessors ====================
238
239    #[inline]
240    pub const fn fetch_borrow_rate(&self) -> bool {
241        self.inner.contains(Capabilities::FETCH_BORROW_RATE)
242    }
243
244    #[inline]
245    pub const fn fetch_borrow_rates(&self) -> bool {
246        self.inner.contains(Capabilities::FETCH_BORROW_RATES)
247    }
248
249    #[inline]
250    pub const fn fetch_funding_rate(&self) -> bool {
251        self.inner.contains(Capabilities::FETCH_FUNDING_RATE)
252    }
253
254    #[inline]
255    pub const fn fetch_funding_rates(&self) -> bool {
256        self.inner.contains(Capabilities::FETCH_FUNDING_RATES)
257    }
258
259    #[inline]
260    pub const fn fetch_positions(&self) -> bool {
261        self.inner.contains(Capabilities::FETCH_POSITIONS)
262    }
263
264    #[inline]
265    pub const fn set_leverage(&self) -> bool {
266        self.inner.contains(Capabilities::SET_LEVERAGE)
267    }
268
269    #[inline]
270    pub const fn set_margin_mode(&self) -> bool {
271        self.inner.contains(Capabilities::SET_MARGIN_MODE)
272    }
273
274    // ==================== WebSocket Accessors ====================
275
276    #[inline]
277    pub const fn websocket(&self) -> bool {
278        self.inner.contains(Capabilities::WEBSOCKET)
279    }
280
281    #[inline]
282    pub const fn watch_ticker(&self) -> bool {
283        self.inner.contains(Capabilities::WATCH_TICKER)
284    }
285
286    #[inline]
287    pub const fn watch_tickers(&self) -> bool {
288        self.inner.contains(Capabilities::WATCH_TICKERS)
289    }
290
291    #[inline]
292    pub const fn watch_order_book(&self) -> bool {
293        self.inner.contains(Capabilities::WATCH_ORDER_BOOK)
294    }
295
296    #[inline]
297    pub const fn watch_trades(&self) -> bool {
298        self.inner.contains(Capabilities::WATCH_TRADES)
299    }
300
301    #[inline]
302    pub const fn watch_ohlcv(&self) -> bool {
303        self.inner.contains(Capabilities::WATCH_OHLCV)
304    }
305
306    #[inline]
307    pub const fn watch_balance(&self) -> bool {
308        self.inner.contains(Capabilities::WATCH_BALANCE)
309    }
310
311    #[inline]
312    pub const fn watch_orders(&self) -> bool {
313        self.inner.contains(Capabilities::WATCH_ORDERS)
314    }
315
316    #[inline]
317    pub const fn watch_my_trades(&self) -> bool {
318        self.inner.contains(Capabilities::WATCH_MY_TRADES)
319    }
320
321    // ==================== Builder Method ====================
322
323    /// Create a builder for ExchangeCapabilities
324    pub fn builder() -> ExchangeCapabilitiesBuilder {
325        ExchangeCapabilitiesBuilder::new()
326    }
327
328    // ==================== Presets ====================
329
330    /// Create capabilities for a typical spot exchange
331    pub const fn spot_exchange() -> Self {
332        Self {
333            inner: Capabilities::from_bits_truncate(
334                Capabilities::MARKET_DATA.bits()
335                    | Capabilities::TRADING.bits()
336                    | Capabilities::ACCOUNT.bits()
337                    | Capabilities::WEBSOCKET.bits()
338                    | Capabilities::WATCH_TICKER.bits()
339                    | Capabilities::WATCH_ORDER_BOOK.bits()
340                    | Capabilities::WATCH_TRADES.bits(),
341            ),
342        }
343    }
344
345    /// Create capabilities for a typical futures exchange
346    pub const fn futures_exchange() -> Self {
347        Self {
348            inner: Capabilities::from_bits_truncate(
349                Capabilities::MARKET_DATA.bits()
350                    | Capabilities::TRADING.bits()
351                    | Capabilities::ACCOUNT.bits()
352                    | Capabilities::MARGIN.bits()
353                    | Capabilities::WEBSOCKET_ALL.bits(),
354            ),
355        }
356    }
357
358    /// Create capabilities for a full-featured exchange (like Binance)
359    pub const fn full_featured() -> Self {
360        Self {
361            inner: Capabilities::ALL,
362        }
363    }
364
365    // ==================== Trait Implementation Checks ====================
366
367    #[inline]
368    pub const fn supports_market_data(&self) -> bool {
369        self.inner
370            .contains(TraitCategory::MarketData.minimum_capabilities())
371    }
372
373    #[inline]
374    pub const fn supports_trading(&self) -> bool {
375        self.inner
376            .contains(TraitCategory::Trading.minimum_capabilities())
377    }
378
379    #[inline]
380    pub const fn supports_account(&self) -> bool {
381        self.inner
382            .contains(TraitCategory::Account.minimum_capabilities())
383    }
384
385    #[inline]
386    pub const fn supports_margin(&self) -> bool {
387        self.inner
388            .contains(TraitCategory::Margin.minimum_capabilities())
389    }
390
391    #[inline]
392    pub const fn supports_funding(&self) -> bool {
393        self.inner
394            .contains(TraitCategory::Funding.minimum_capabilities())
395    }
396
397    #[inline]
398    pub const fn supports_websocket(&self) -> bool {
399        self.inner
400            .contains(TraitCategory::WebSocket.minimum_capabilities())
401    }
402
403    #[inline]
404    pub const fn supports_full_exchange(&self) -> bool {
405        self.supports_market_data()
406            && self.supports_trading()
407            && self.supports_account()
408            && self.supports_margin()
409            && self.supports_funding()
410    }
411
412    pub const fn supports_trait(&self, category: TraitCategory) -> bool {
413        match category {
414            TraitCategory::PublicExchange => true,
415            TraitCategory::MarketData => self.supports_market_data(),
416            TraitCategory::Trading => self.supports_trading(),
417            TraitCategory::Account => self.supports_account(),
418            TraitCategory::Margin => self.supports_margin(),
419            TraitCategory::Funding => self.supports_funding(),
420            TraitCategory::WebSocket => self.supports_websocket(),
421        }
422    }
423
424    pub fn supported_traits(&self) -> Vec<TraitCategory> {
425        TraitCategory::all()
426            .iter()
427            .filter(|cat| self.supports_trait(**cat))
428            .copied()
429            .collect()
430    }
431
432    pub const fn capabilities_for_trait(&self, category: TraitCategory) -> Capabilities {
433        Capabilities::from_bits_truncate(self.inner.bits() & category.capabilities().bits())
434    }
435
436    pub const fn trait_for_capability(capability: Capability) -> TraitCategory {
437        capability.trait_category()
438    }
439}
440
441impl From<Capabilities> for ExchangeCapabilities {
442    fn from(caps: Capabilities) -> Self {
443        Self::from_capabilities(caps)
444    }
445}
446
447impl fmt::Display for ExchangeCapabilities {
448    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449        write!(f, "ExchangeCapabilities({})", self.inner)
450    }
451}
452
453/// Builder for ExchangeCapabilities
454#[derive(Debug, Clone, Default)]
455pub struct ExchangeCapabilitiesBuilder {
456    inner: Capabilities,
457}
458
459impl ExchangeCapabilitiesBuilder {
460    pub fn new() -> Self {
461        Self {
462            inner: Capabilities::empty(),
463        }
464    }
465
466    pub fn market_data(mut self) -> Self {
467        self.inner |= Capabilities::MARKET_DATA;
468        self
469    }
470
471    pub fn trading(mut self) -> Self {
472        self.inner |= Capabilities::TRADING;
473        self
474    }
475
476    pub fn account(mut self) -> Self {
477        self.inner |= Capabilities::ACCOUNT;
478        self
479    }
480
481    pub fn funding(mut self) -> Self {
482        self.inner |= Capabilities::FUNDING;
483        self
484    }
485
486    pub fn margin(mut self) -> Self {
487        self.inner |= Capabilities::MARGIN;
488        self
489    }
490
491    pub fn websocket_all(mut self) -> Self {
492        self.inner |= Capabilities::WEBSOCKET_ALL;
493        self
494    }
495
496    pub fn websocket(mut self) -> Self {
497        self.inner |= Capabilities::WEBSOCKET;
498        self
499    }
500
501    pub fn rest_all(mut self) -> Self {
502        self.inner |= Capabilities::REST_ALL;
503        self
504    }
505
506    pub fn all(mut self) -> Self {
507        self.inner = Capabilities::ALL;
508        self
509    }
510
511    pub fn capability(mut self, cap: Capability) -> Self {
512        self.inner |= Capabilities::from(cap);
513        self
514    }
515
516    pub fn capabilities<I: IntoIterator<Item = Capability>>(mut self, caps: I) -> Self {
517        for cap in caps {
518            self.inner |= Capabilities::from(cap);
519        }
520        self
521    }
522
523    pub fn raw(mut self, caps: Capabilities) -> Self {
524        self.inner |= caps;
525        self
526    }
527
528    pub fn without_capability(mut self, cap: Capability) -> Self {
529        self.inner.remove(Capabilities::from(cap));
530        self
531    }
532
533    pub fn without(mut self, caps: Capabilities) -> Self {
534        self.inner.remove(caps);
535        self
536    }
537
538    pub fn build(self) -> ExchangeCapabilities {
539        ExchangeCapabilities { inner: self.inner }
540    }
541}
542
543/// Trait category for capability-to-trait mapping
544#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
545pub enum TraitCategory {
546    PublicExchange,
547    MarketData,
548    Trading,
549    Account,
550    Margin,
551    Funding,
552    WebSocket,
553}
554
555impl TraitCategory {
556    pub const fn all() -> [Self; 7] {
557        [
558            Self::PublicExchange,
559            Self::MarketData,
560            Self::Trading,
561            Self::Account,
562            Self::Margin,
563            Self::Funding,
564            Self::WebSocket,
565        ]
566    }
567
568    pub const fn name(&self) -> &'static str {
569        match self {
570            Self::PublicExchange => "PublicExchange",
571            Self::MarketData => "MarketData",
572            Self::Trading => "Trading",
573            Self::Account => "Account",
574            Self::Margin => "Margin",
575            Self::Funding => "Funding",
576            Self::WebSocket => "WebSocket",
577        }
578    }
579
580    pub const fn capabilities(&self) -> Capabilities {
581        match self {
582            Self::PublicExchange => Capabilities::empty(),
583            Self::MarketData => Capabilities::MARKET_DATA,
584            Self::Trading => Capabilities::TRADING,
585            Self::Account => Capabilities::ACCOUNT,
586            Self::Margin => Capabilities::MARGIN,
587            Self::Funding => Capabilities::FUNDING,
588            Self::WebSocket => Capabilities::WEBSOCKET_ALL,
589        }
590    }
591
592    pub const fn minimum_capabilities(&self) -> Capabilities {
593        match self {
594            Self::PublicExchange => Capabilities::empty(),
595            Self::MarketData => Capabilities::from_bits_truncate(
596                Capabilities::FETCH_MARKETS.bits() | Capabilities::FETCH_TICKER.bits(),
597            ),
598            Self::Trading => Capabilities::from_bits_truncate(
599                Capabilities::CREATE_ORDER.bits() | Capabilities::CANCEL_ORDER.bits(),
600            ),
601            Self::Account => Capabilities::FETCH_BALANCE,
602            Self::Margin => Capabilities::FETCH_POSITIONS,
603            Self::Funding => Capabilities::FETCH_DEPOSIT_ADDRESS,
604            Self::WebSocket => Capabilities::WEBSOCKET,
605        }
606    }
607}
608
609impl fmt::Display for TraitCategory {
610    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
611        write!(f, "{}", self.name())
612    }
613}