1use std::sync::Arc;
68
69mod account;
71mod funding;
72mod margin;
73mod market_data;
74mod public_exchange;
75mod trading;
76
77pub use account::{Account, BoxedAccount};
79pub use funding::{BoxedFunding, Funding};
80pub use margin::{BoxedMargin, Margin};
81pub use market_data::{BoxedMarketData, MarketData};
82pub use public_exchange::PublicExchange;
83pub use trading::{BoxedTrading, Trading};
84
85pub trait FullExchange: PublicExchange + MarketData + Trading + Account + Margin + Funding {}
108
109impl<T> FullExchange for T where
111 T: PublicExchange + MarketData + Trading + Account + Margin + Funding
112{
113}
114
115pub type BoxedFullExchange = Box<dyn FullExchange>;
130
131pub type ArcFullExchange = Arc<dyn FullExchange>;
146
147pub type BoxedPublicExchange = Box<dyn PublicExchange>;
149
150pub type ArcPublicExchange = Arc<dyn PublicExchange>;
152
153pub type ArcMarketData = Arc<dyn MarketData>;
155
156pub type ArcTrading = Arc<dyn Trading>;
158
159pub type ArcAccount = Arc<dyn Account>;
161
162pub type ArcMargin = Arc<dyn Margin>;
164
165pub type ArcFunding = Arc<dyn Funding>;
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use crate::capability::ExchangeCapabilities;
172 use crate::error::Result;
173 use crate::types::{
174 Balance, BalanceEntry, DepositAddress, FundingRate, FundingRateHistory, Market, Ohlcv,
175 Order, OrderBook, Position, Ticker, Timeframe, Trade, Transaction, Transfer,
176 params::{
177 BalanceParams, LeverageParams, MarginMode, OhlcvParams, OrderBookParams, OrderParams,
178 TransferParams, WithdrawParams,
179 },
180 transaction::{TransactionStatus, TransactionType},
181 };
182 use async_trait::async_trait;
183 use rust_decimal_macros::dec;
184 use std::collections::HashMap;
185
186 struct MockFullExchange;
188
189 impl PublicExchange for MockFullExchange {
190 fn id(&self) -> &str {
191 "mock_full"
192 }
193 fn name(&self) -> &str {
194 "Mock Full Exchange"
195 }
196 fn capabilities(&self) -> ExchangeCapabilities {
197 ExchangeCapabilities::all()
198 }
199 fn timeframes(&self) -> Vec<Timeframe> {
200 vec![Timeframe::H1, Timeframe::D1]
201 }
202 }
203
204 #[async_trait]
205 impl MarketData for MockFullExchange {
206 async fn fetch_markets(&self) -> Result<Vec<Market>> {
207 Ok(vec![])
208 }
209
210 async fn load_markets_with_reload(
211 &self,
212 _reload: bool,
213 ) -> Result<Arc<HashMap<String, Arc<Market>>>> {
214 Ok(Arc::new(HashMap::new()))
215 }
216
217 async fn fetch_ticker(&self, symbol: &str) -> Result<Ticker> {
218 Ok(Ticker {
219 symbol: symbol.to_string(),
220 ..Default::default()
221 })
222 }
223
224 async fn fetch_tickers(&self, _symbols: &[&str]) -> Result<Vec<Ticker>> {
225 Ok(vec![])
226 }
227
228 async fn fetch_order_book_with_params(
229 &self,
230 symbol: &str,
231 _params: OrderBookParams,
232 ) -> Result<OrderBook> {
233 Ok(OrderBook::new(symbol.to_string(), 0))
234 }
235
236 async fn fetch_trades_with_limit(
237 &self,
238 _symbol: &str,
239 _since: Option<i64>,
240 _limit: Option<u32>,
241 ) -> Result<Vec<Trade>> {
242 Ok(vec![])
243 }
244
245 async fn fetch_ohlcv_with_params(
246 &self,
247 _symbol: &str,
248 _params: OhlcvParams,
249 ) -> Result<Vec<Ohlcv>> {
250 Ok(vec![])
251 }
252
253 async fn market(&self, symbol: &str) -> Result<Arc<Market>> {
254 Ok(Arc::new(Market {
255 symbol: symbol.to_string(),
256 ..Default::default()
257 }))
258 }
259
260 async fn markets(&self) -> Arc<HashMap<String, Arc<Market>>> {
261 Arc::new(HashMap::new())
262 }
263 }
264
265 #[async_trait]
266 impl Trading for MockFullExchange {
267 async fn create_order(&self, params: OrderParams) -> Result<Order> {
268 Ok(Order::new(
269 "order_123".to_string(),
270 params.symbol,
271 params.order_type,
272 params.side,
273 params.amount,
274 params.price,
275 crate::types::OrderStatus::Open,
276 ))
277 }
278
279 async fn cancel_order(&self, id: &str, symbol: &str) -> Result<Order> {
280 Ok(Order::new(
281 id.to_string(),
282 symbol.to_string(),
283 crate::types::OrderType::Limit,
284 crate::types::OrderSide::Buy,
285 dec!(0),
286 None,
287 crate::types::OrderStatus::Cancelled,
288 ))
289 }
290
291 async fn cancel_all_orders(&self, _symbol: &str) -> Result<Vec<Order>> {
292 Ok(vec![])
293 }
294
295 async fn fetch_order(&self, id: &str, symbol: &str) -> Result<Order> {
296 Ok(Order::new(
297 id.to_string(),
298 symbol.to_string(),
299 crate::types::OrderType::Limit,
300 crate::types::OrderSide::Buy,
301 dec!(0),
302 None,
303 crate::types::OrderStatus::Open,
304 ))
305 }
306
307 async fn fetch_open_orders(&self, _symbol: Option<&str>) -> Result<Vec<Order>> {
308 Ok(vec![])
309 }
310
311 async fn fetch_closed_orders(
312 &self,
313 _symbol: Option<&str>,
314 _since: Option<i64>,
315 _limit: Option<u32>,
316 ) -> Result<Vec<Order>> {
317 Ok(vec![])
318 }
319 }
320
321 #[async_trait]
322 impl Account for MockFullExchange {
323 async fn fetch_balance_with_params(&self, _params: BalanceParams) -> Result<Balance> {
324 let mut balance = Balance::new();
325 balance.set("USDT".to_string(), BalanceEntry::new(dec!(10000), dec!(0)));
326 Ok(balance)
327 }
328
329 async fn fetch_my_trades_since(
330 &self,
331 _symbol: &str,
332 _since: Option<i64>,
333 _limit: Option<u32>,
334 ) -> Result<Vec<Trade>> {
335 Ok(vec![])
336 }
337 }
338
339 #[async_trait]
340 impl Margin for MockFullExchange {
341 async fn fetch_positions_for(&self, _symbols: &[&str]) -> Result<Vec<Position>> {
342 Ok(vec![])
343 }
344
345 async fn fetch_position(&self, symbol: &str) -> Result<Position> {
346 Ok(Position {
347 symbol: symbol.to_string(),
348 ..Default::default()
349 })
350 }
351
352 async fn set_leverage_with_params(&self, _params: LeverageParams) -> Result<()> {
353 Ok(())
354 }
355
356 async fn get_leverage(&self, _symbol: &str) -> Result<u32> {
357 Ok(10)
358 }
359
360 async fn set_margin_mode(&self, _symbol: &str, _mode: MarginMode) -> Result<()> {
361 Ok(())
362 }
363
364 async fn fetch_funding_rate(&self, symbol: &str) -> Result<FundingRate> {
365 Ok(FundingRate {
366 symbol: symbol.to_string(),
367 ..Default::default()
368 })
369 }
370
371 async fn fetch_funding_rates(&self, _symbols: &[&str]) -> Result<Vec<FundingRate>> {
372 Ok(vec![])
373 }
374
375 async fn fetch_funding_rate_history(
376 &self,
377 _symbol: &str,
378 _since: Option<i64>,
379 _limit: Option<u32>,
380 ) -> Result<Vec<FundingRateHistory>> {
381 Ok(vec![])
382 }
383 }
384
385 #[async_trait]
386 impl Funding for MockFullExchange {
387 async fn fetch_deposit_address(&self, code: &str) -> Result<DepositAddress> {
388 Ok(DepositAddress::new(code.to_string(), "0x123".to_string()))
389 }
390
391 async fn fetch_deposit_address_on_network(
392 &self,
393 code: &str,
394 network: &str,
395 ) -> Result<DepositAddress> {
396 let mut addr = DepositAddress::new(code.to_string(), "0x123".to_string());
397 addr.network = Some(network.to_string());
398 Ok(addr)
399 }
400
401 async fn withdraw(&self, params: WithdrawParams) -> Result<Transaction> {
402 Ok(Transaction::new(
403 "tx_123".to_string(),
404 TransactionType::Withdrawal,
405 params.amount,
406 params.currency,
407 TransactionStatus::Pending,
408 ))
409 }
410
411 async fn transfer(&self, params: TransferParams) -> Result<Transfer> {
412 Ok(Transfer {
413 id: Some("transfer_123".to_string()),
414 timestamp: 0,
415 datetime: "".to_string(),
416 currency: params.currency,
417 amount: 0.0,
418 from_account: None,
419 to_account: None,
420 status: "success".to_string(),
421 info: None,
422 })
423 }
424
425 async fn fetch_deposits(
426 &self,
427 _code: Option<&str>,
428 _since: Option<i64>,
429 _limit: Option<u32>,
430 ) -> Result<Vec<Transaction>> {
431 Ok(vec![])
432 }
433
434 async fn fetch_withdrawals(
435 &self,
436 _code: Option<&str>,
437 _since: Option<i64>,
438 _limit: Option<u32>,
439 ) -> Result<Vec<Transaction>> {
440 Ok(vec![])
441 }
442 }
443
444 #[test]
445 fn test_full_exchange_blanket_impl() {
446 fn assert_full_exchange<T: FullExchange>(_: &T) {}
448 let exchange = MockFullExchange;
449 assert_full_exchange(&exchange);
450 }
451
452 #[test]
453 fn test_boxed_full_exchange() {
454 let _exchange: BoxedFullExchange = Box::new(MockFullExchange);
455 }
456
457 #[test]
458 fn test_arc_full_exchange() {
459 let _exchange: ArcFullExchange = Arc::new(MockFullExchange);
460 }
461
462 #[tokio::test]
463 async fn test_full_exchange_methods() {
464 let exchange = MockFullExchange;
465
466 assert_eq!(exchange.id(), "mock_full");
468
469 let ticker = exchange.fetch_ticker("BTC/USDT").await.unwrap();
470 assert_eq!(ticker.symbol, "BTC/USDT");
471
472 let balance = exchange.fetch_balance().await.unwrap();
473 assert!(balance.get("USDT").is_some());
474
475 let positions = exchange.fetch_positions().await.unwrap();
476 assert!(positions.is_empty());
477
478 let address = exchange.fetch_deposit_address("USDT").await.unwrap();
479 assert_eq!(address.currency, "USDT");
480 }
481
482 #[test]
483 fn test_arc_type_aliases() {
484 let exchange = Arc::new(MockFullExchange);
485
486 let _: ArcFullExchange = exchange.clone();
488 let _: ArcMarketData = exchange.clone();
489 let _: ArcTrading = exchange.clone();
490 let _: ArcAccount = exchange.clone();
491 let _: ArcMargin = exchange.clone();
492 let _: ArcFunding = exchange.clone();
493 }
494}