1#![allow(missing_docs)]
8use std::fmt;
32
33mod exchange_caps;
34mod flags;
35mod macros;
36
37pub use exchange_caps::{ExchangeCapabilities, ExchangeCapabilitiesBuilder, TraitCategory};
38pub use flags::Capabilities;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59#[repr(u8)]
60pub enum Capability {
61 FetchMarkets = 0,
63 FetchCurrencies = 1,
64 FetchTicker = 2,
65 FetchTickers = 3,
66 FetchOrderBook = 4,
67 FetchTrades = 5,
68 FetchOhlcv = 6,
69 FetchStatus = 7,
70 FetchTime = 8,
71
72 CreateOrder = 9,
74 CreateMarketOrder = 10,
75 CreateLimitOrder = 11,
76 CancelOrder = 12,
77 CancelAllOrders = 13,
78 EditOrder = 14,
79 FetchOrder = 15,
80 FetchOrders = 16,
81 FetchOpenOrders = 17,
82 FetchClosedOrders = 18,
83 FetchCanceledOrders = 19,
84
85 FetchBalance = 20,
87 FetchMyTrades = 21,
88 FetchDeposits = 22,
89 FetchWithdrawals = 23,
90 FetchTransactions = 24,
91 FetchLedger = 25,
92
93 FetchDepositAddress = 26,
95 CreateDepositAddress = 27,
96 Withdraw = 28,
97 Transfer = 29,
98
99 FetchBorrowRate = 30,
101 FetchBorrowRates = 31,
102 FetchFundingRate = 32,
103 FetchFundingRates = 33,
104 FetchPositions = 34,
105 SetLeverage = 35,
106 SetMarginMode = 36,
107
108 Websocket = 37,
110 WatchTicker = 38,
111 WatchTickers = 39,
112 WatchOrderBook = 40,
113 WatchTrades = 41,
114 WatchOhlcv = 42,
115 WatchBalance = 43,
116 WatchOrders = 44,
117 WatchMyTrades = 45,
118}
119
120impl Capability {
121 pub const COUNT: usize = 46;
123
124 pub const fn as_ccxt_name(&self) -> &'static str {
126 match self {
127 Self::FetchMarkets => "fetchMarkets",
129 Self::FetchCurrencies => "fetchCurrencies",
130 Self::FetchTicker => "fetchTicker",
131 Self::FetchTickers => "fetchTickers",
132 Self::FetchOrderBook => "fetchOrderBook",
133 Self::FetchTrades => "fetchTrades",
134 Self::FetchOhlcv => "fetchOHLCV",
135 Self::FetchStatus => "fetchStatus",
136 Self::FetchTime => "fetchTime",
137 Self::CreateOrder => "createOrder",
139 Self::CreateMarketOrder => "createMarketOrder",
140 Self::CreateLimitOrder => "createLimitOrder",
141 Self::CancelOrder => "cancelOrder",
142 Self::CancelAllOrders => "cancelAllOrders",
143 Self::EditOrder => "editOrder",
144 Self::FetchOrder => "fetchOrder",
145 Self::FetchOrders => "fetchOrders",
146 Self::FetchOpenOrders => "fetchOpenOrders",
147 Self::FetchClosedOrders => "fetchClosedOrders",
148 Self::FetchCanceledOrders => "fetchCanceledOrders",
149 Self::FetchBalance => "fetchBalance",
151 Self::FetchMyTrades => "fetchMyTrades",
152 Self::FetchDeposits => "fetchDeposits",
153 Self::FetchWithdrawals => "fetchWithdrawals",
154 Self::FetchTransactions => "fetchTransactions",
155 Self::FetchLedger => "fetchLedger",
156 Self::FetchDepositAddress => "fetchDepositAddress",
158 Self::CreateDepositAddress => "createDepositAddress",
159 Self::Withdraw => "withdraw",
160 Self::Transfer => "transfer",
161 Self::FetchBorrowRate => "fetchBorrowRate",
163 Self::FetchBorrowRates => "fetchBorrowRates",
164 Self::FetchFundingRate => "fetchFundingRate",
165 Self::FetchFundingRates => "fetchFundingRates",
166 Self::FetchPositions => "fetchPositions",
167 Self::SetLeverage => "setLeverage",
168 Self::SetMarginMode => "setMarginMode",
169 Self::Websocket => "websocket",
171 Self::WatchTicker => "watchTicker",
172 Self::WatchTickers => "watchTickers",
173 Self::WatchOrderBook => "watchOrderBook",
174 Self::WatchTrades => "watchTrades",
175 Self::WatchOhlcv => "watchOHLCV",
176 Self::WatchBalance => "watchBalance",
177 Self::WatchOrders => "watchOrders",
178 Self::WatchMyTrades => "watchMyTrades",
179 }
180 }
181
182 pub fn from_ccxt_name(name: &str) -> Option<Self> {
184 match name {
185 "fetchMarkets" => Some(Self::FetchMarkets),
187 "fetchCurrencies" => Some(Self::FetchCurrencies),
188 "fetchTicker" => Some(Self::FetchTicker),
189 "fetchTickers" => Some(Self::FetchTickers),
190 "fetchOrderBook" => Some(Self::FetchOrderBook),
191 "fetchTrades" => Some(Self::FetchTrades),
192 "fetchOHLCV" => Some(Self::FetchOhlcv),
193 "fetchStatus" => Some(Self::FetchStatus),
194 "fetchTime" => Some(Self::FetchTime),
195 "createOrder" => Some(Self::CreateOrder),
197 "createMarketOrder" => Some(Self::CreateMarketOrder),
198 "createLimitOrder" => Some(Self::CreateLimitOrder),
199 "cancelOrder" => Some(Self::CancelOrder),
200 "cancelAllOrders" => Some(Self::CancelAllOrders),
201 "editOrder" => Some(Self::EditOrder),
202 "fetchOrder" => Some(Self::FetchOrder),
203 "fetchOrders" => Some(Self::FetchOrders),
204 "fetchOpenOrders" => Some(Self::FetchOpenOrders),
205 "fetchClosedOrders" => Some(Self::FetchClosedOrders),
206 "fetchCanceledOrders" => Some(Self::FetchCanceledOrders),
207 "fetchBalance" => Some(Self::FetchBalance),
209 "fetchMyTrades" => Some(Self::FetchMyTrades),
210 "fetchDeposits" => Some(Self::FetchDeposits),
211 "fetchWithdrawals" => Some(Self::FetchWithdrawals),
212 "fetchTransactions" => Some(Self::FetchTransactions),
213 "fetchLedger" => Some(Self::FetchLedger),
214 "fetchDepositAddress" => Some(Self::FetchDepositAddress),
216 "createDepositAddress" => Some(Self::CreateDepositAddress),
217 "withdraw" => Some(Self::Withdraw),
218 "transfer" => Some(Self::Transfer),
219 "fetchBorrowRate" => Some(Self::FetchBorrowRate),
221 "fetchBorrowRates" => Some(Self::FetchBorrowRates),
222 "fetchFundingRate" => Some(Self::FetchFundingRate),
223 "fetchFundingRates" => Some(Self::FetchFundingRates),
224 "fetchPositions" => Some(Self::FetchPositions),
225 "setLeverage" => Some(Self::SetLeverage),
226 "setMarginMode" => Some(Self::SetMarginMode),
227 "websocket" => Some(Self::Websocket),
229 "watchTicker" => Some(Self::WatchTicker),
230 "watchTickers" => Some(Self::WatchTickers),
231 "watchOrderBook" => Some(Self::WatchOrderBook),
232 "watchTrades" => Some(Self::WatchTrades),
233 "watchOHLCV" => Some(Self::WatchOhlcv),
234 "watchBalance" => Some(Self::WatchBalance),
235 "watchOrders" => Some(Self::WatchOrders),
236 "watchMyTrades" => Some(Self::WatchMyTrades),
237 _ => None,
238 }
239 }
240
241 pub const fn all() -> [Self; Self::COUNT] {
243 [
244 Self::FetchMarkets,
245 Self::FetchCurrencies,
246 Self::FetchTicker,
247 Self::FetchTickers,
248 Self::FetchOrderBook,
249 Self::FetchTrades,
250 Self::FetchOhlcv,
251 Self::FetchStatus,
252 Self::FetchTime,
253 Self::CreateOrder,
254 Self::CreateMarketOrder,
255 Self::CreateLimitOrder,
256 Self::CancelOrder,
257 Self::CancelAllOrders,
258 Self::EditOrder,
259 Self::FetchOrder,
260 Self::FetchOrders,
261 Self::FetchOpenOrders,
262 Self::FetchClosedOrders,
263 Self::FetchCanceledOrders,
264 Self::FetchBalance,
265 Self::FetchMyTrades,
266 Self::FetchDeposits,
267 Self::FetchWithdrawals,
268 Self::FetchTransactions,
269 Self::FetchLedger,
270 Self::FetchDepositAddress,
271 Self::CreateDepositAddress,
272 Self::Withdraw,
273 Self::Transfer,
274 Self::FetchBorrowRate,
275 Self::FetchBorrowRates,
276 Self::FetchFundingRate,
277 Self::FetchFundingRates,
278 Self::FetchPositions,
279 Self::SetLeverage,
280 Self::SetMarginMode,
281 Self::Websocket,
282 Self::WatchTicker,
283 Self::WatchTickers,
284 Self::WatchOrderBook,
285 Self::WatchTrades,
286 Self::WatchOhlcv,
287 Self::WatchBalance,
288 Self::WatchOrders,
289 Self::WatchMyTrades,
290 ]
291 }
292
293 #[inline]
295 pub const fn bit_position(&self) -> u64 {
296 1u64 << (*self as u8)
297 }
298
299 pub const fn trait_category(&self) -> TraitCategory {
301 match self {
302 Self::FetchMarkets
304 | Self::FetchCurrencies
305 | Self::FetchTicker
306 | Self::FetchTickers
307 | Self::FetchOrderBook
308 | Self::FetchTrades
309 | Self::FetchOhlcv
310 | Self::FetchStatus
311 | Self::FetchTime => TraitCategory::MarketData,
312
313 Self::CreateOrder
315 | Self::CreateMarketOrder
316 | Self::CreateLimitOrder
317 | Self::CancelOrder
318 | Self::CancelAllOrders
319 | Self::EditOrder
320 | Self::FetchOrder
321 | Self::FetchOrders
322 | Self::FetchOpenOrders
323 | Self::FetchClosedOrders
324 | Self::FetchCanceledOrders => TraitCategory::Trading,
325
326 Self::FetchBalance
328 | Self::FetchMyTrades
329 | Self::FetchDeposits
330 | Self::FetchWithdrawals
331 | Self::FetchTransactions
332 | Self::FetchLedger => TraitCategory::Account,
333
334 Self::FetchDepositAddress
336 | Self::CreateDepositAddress
337 | Self::Withdraw
338 | Self::Transfer => TraitCategory::Funding,
339
340 Self::FetchBorrowRate
342 | Self::FetchBorrowRates
343 | Self::FetchFundingRate
344 | Self::FetchFundingRates
345 | Self::FetchPositions
346 | Self::SetLeverage
347 | Self::SetMarginMode => TraitCategory::Margin,
348
349 Self::Websocket
351 | Self::WatchTicker
352 | Self::WatchTickers
353 | Self::WatchOrderBook
354 | Self::WatchTrades
355 | Self::WatchOhlcv
356 | Self::WatchBalance
357 | Self::WatchOrders
358 | Self::WatchMyTrades => TraitCategory::WebSocket,
359 }
360 }
361}
362
363impl fmt::Display for Capability {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 write!(f, "{}", self.as_ccxt_name())
366 }
367}
368
369#[cfg(test)]
374mod tests {
375 use super::*;
376 use crate::capabilities;
377
378 #[test]
379 fn test_capability_count() {
380 assert_eq!(Capability::COUNT, 46);
381 }
382
383 #[test]
384 fn test_capability_ccxt_names() {
385 assert_eq!(Capability::FetchTicker.as_ccxt_name(), "fetchTicker");
386 assert_eq!(Capability::CreateOrder.as_ccxt_name(), "createOrder");
387 assert_eq!(Capability::FetchOhlcv.as_ccxt_name(), "fetchOHLCV");
388 assert_eq!(Capability::WatchOhlcv.as_ccxt_name(), "watchOHLCV");
389 }
390
391 #[test]
392 fn test_capability_from_ccxt_name() {
393 assert_eq!(
394 Capability::from_ccxt_name("fetchTicker"),
395 Some(Capability::FetchTicker)
396 );
397 assert_eq!(
398 Capability::from_ccxt_name("createOrder"),
399 Some(Capability::CreateOrder)
400 );
401 assert_eq!(Capability::from_ccxt_name("unknown"), None);
402 }
403
404 #[test]
405 fn test_capabilities_bitflags() {
406 let caps = Capabilities::FETCH_TICKER | Capabilities::CREATE_ORDER;
407 assert!(caps.contains(Capabilities::FETCH_TICKER));
408 assert!(caps.contains(Capabilities::CREATE_ORDER));
409 assert!(!caps.contains(Capabilities::WEBSOCKET));
410 }
411
412 #[test]
413 fn test_capabilities_presets() {
414 assert!(Capabilities::MARKET_DATA.contains(Capabilities::FETCH_TICKER));
415 assert!(Capabilities::MARKET_DATA.contains(Capabilities::FETCH_ORDER_BOOK));
416 assert!(!Capabilities::MARKET_DATA.contains(Capabilities::CREATE_ORDER));
417
418 assert!(Capabilities::TRADING.contains(Capabilities::CREATE_ORDER));
419 assert!(Capabilities::TRADING.contains(Capabilities::CANCEL_ORDER));
420 assert!(!Capabilities::TRADING.contains(Capabilities::FETCH_TICKER));
421 }
422
423 #[test]
424 fn test_capabilities_has() {
425 let caps = Capabilities::MARKET_DATA;
426 assert!(caps.has("fetchTicker"));
427 assert!(caps.has("fetchOrderBook"));
428 assert!(!caps.has("createOrder"));
429 assert!(!caps.has("unknownCapability"));
430 }
431
432 #[test]
433 fn test_capabilities_count() {
434 assert_eq!(Capabilities::empty().count(), 0);
435 assert_eq!(Capabilities::FETCH_TICKER.count(), 1);
436 assert_eq!(
437 (Capabilities::FETCH_TICKER | Capabilities::CREATE_ORDER).count(),
438 2
439 );
440 assert_eq!(Capabilities::MARKET_DATA.count(), 9);
441 }
442
443 #[test]
444 fn test_capabilities_from_iter() {
445 let caps = Capabilities::from_iter([
446 Capability::FetchTicker,
447 Capability::CreateOrder,
448 Capability::Websocket,
449 ]);
450 assert!(caps.contains(Capabilities::FETCH_TICKER));
451 assert!(caps.contains(Capabilities::CREATE_ORDER));
452 assert!(caps.contains(Capabilities::WEBSOCKET));
453 assert_eq!(caps.count(), 3);
454 }
455
456 #[test]
457 fn test_exchange_capabilities_all() {
458 let caps = ExchangeCapabilities::all();
459 assert!(caps.fetch_ticker());
460 assert!(caps.create_order());
461 assert!(caps.websocket());
462 assert!(caps.fetch_positions());
463 }
464
465 #[test]
466 fn test_exchange_capabilities_public_only() {
467 let caps = ExchangeCapabilities::public_only();
468 assert!(caps.fetch_ticker());
469 assert!(caps.fetch_order_book());
470 assert!(!caps.create_order());
471 assert!(!caps.fetch_balance());
472 }
473
474 #[test]
475 fn test_exchange_capabilities_has() {
476 let caps = ExchangeCapabilities::all();
477 assert!(caps.has("fetchTicker"));
478 assert!(caps.has("createOrder"));
479 assert!(!caps.has("unknownCapability"));
480 }
481
482 #[test]
483 fn test_exchange_capabilities_builder() {
484 let caps = ExchangeCapabilities::builder()
485 .market_data()
486 .trading()
487 .build();
488
489 assert!(caps.fetch_ticker());
490 assert!(caps.create_order());
491 assert!(!caps.websocket());
492 }
493
494 #[test]
495 fn test_exchange_capabilities_builder_with_capability() {
496 let caps = ExchangeCapabilities::builder()
497 .capability(Capability::FetchTicker)
498 .capability(Capability::Websocket)
499 .build();
500
501 assert!(caps.fetch_ticker());
502 assert!(caps.websocket());
503 assert!(!caps.create_order());
504 }
505
506 #[test]
507 fn test_exchange_capabilities_builder_without() {
508 let caps = ExchangeCapabilities::builder()
509 .all()
510 .without_capability(Capability::Websocket)
511 .build();
512
513 assert!(caps.fetch_ticker());
514 assert!(caps.create_order());
515 assert!(!caps.websocket());
516 }
517
518 #[test]
519 fn test_exchange_capabilities_presets() {
520 let spot = ExchangeCapabilities::spot_exchange();
521 assert!(spot.fetch_ticker());
522 assert!(spot.create_order());
523 assert!(spot.websocket());
524 assert!(!spot.fetch_positions());
525
526 let futures = ExchangeCapabilities::futures_exchange();
527 assert!(futures.fetch_ticker());
528 assert!(futures.create_order());
529 assert!(futures.fetch_positions());
530 assert!(futures.set_leverage());
531 }
532
533 #[test]
534 fn test_capabilities_macro() {
535 let caps = capabilities!(MARKET_DATA);
536 assert!(caps.contains(Capabilities::FETCH_TICKER));
537
538 let caps = capabilities!(FETCH_TICKER | CREATE_ORDER);
539 assert!(caps.contains(Capabilities::FETCH_TICKER));
540 assert!(caps.contains(Capabilities::CREATE_ORDER));
541
542 let caps = capabilities!(MARKET_DATA, TRADING);
543 assert!(caps.contains(Capabilities::FETCH_TICKER));
544 assert!(caps.contains(Capabilities::CREATE_ORDER));
545 }
546
547 #[test]
548 fn test_capability_bit_positions() {
549 assert_eq!(Capability::FetchMarkets.bit_position(), 1 << 0);
550 assert_eq!(Capability::FetchTicker.bit_position(), 1 << 2);
551 assert_eq!(Capability::CreateOrder.bit_position(), 1 << 9);
552 assert_eq!(Capability::Websocket.bit_position(), 1 << 37);
553 }
554
555 #[test]
556 fn test_memory_efficiency() {
557 assert_eq!(std::mem::size_of::<ExchangeCapabilities>(), 8);
558 assert_eq!(std::mem::size_of::<Capabilities>(), 8);
559 }
560
561 #[test]
562 fn test_trait_category_all() {
563 let categories = TraitCategory::all();
564 assert_eq!(categories.len(), 7);
565 assert!(categories.contains(&TraitCategory::PublicExchange));
566 assert!(categories.contains(&TraitCategory::MarketData));
567 assert!(categories.contains(&TraitCategory::Trading));
568 assert!(categories.contains(&TraitCategory::Account));
569 assert!(categories.contains(&TraitCategory::Margin));
570 assert!(categories.contains(&TraitCategory::Funding));
571 assert!(categories.contains(&TraitCategory::WebSocket));
572 }
573
574 #[test]
575 fn test_capability_trait_category() {
576 assert_eq!(
577 Capability::FetchTicker.trait_category(),
578 TraitCategory::MarketData
579 );
580 assert_eq!(
581 Capability::CreateOrder.trait_category(),
582 TraitCategory::Trading
583 );
584 assert_eq!(
585 Capability::FetchBalance.trait_category(),
586 TraitCategory::Account
587 );
588 }
589
590 #[test]
591 fn test_supports_market_data() {
592 let caps = ExchangeCapabilities::public_only();
593 assert!(caps.supports_market_data());
594
595 let caps = ExchangeCapabilities::none();
596 assert!(!caps.supports_market_data());
597 }
598
599 #[test]
600 fn test_supports_trading() {
601 let caps = ExchangeCapabilities::all();
602 assert!(caps.supports_trading());
603
604 let caps = ExchangeCapabilities::public_only();
605 assert!(!caps.supports_trading());
606 }
607
608 #[test]
609 fn test_supports_full_exchange() {
610 let caps = ExchangeCapabilities::all();
611 assert!(caps.supports_full_exchange());
612
613 let caps = ExchangeCapabilities::spot_exchange();
614 assert!(!caps.supports_full_exchange());
615 }
616}