trader_bot/exchange/
provider.rs1use async_trait::async_trait;
4
5use super::{ExchangeInfo, ExchangeResult, Instrument, MarketData, OrderBook};
6use crate::domain::{Order, OrderDirection, Wallet};
7
8#[async_trait]
13pub trait ExchangeProvider: Send + Sync {
14 fn info(&self) -> &ExchangeInfo;
16
17 fn id(&self) -> &str {
19 &self.info().id
20 }
21
22 fn name(&self) -> &str {
24 &self.info().name
25 }
26
27 async fn ping(&self) -> ExchangeResult<()>;
29
30 async fn get_wallet(&self, account_id: &str) -> ExchangeResult<Wallet>;
32
33 async fn get_instrument(&self, symbol: &str) -> ExchangeResult<Instrument>;
35
36 async fn list_instruments(&self) -> ExchangeResult<Vec<Instrument>>;
38
39 async fn get_market_data(&self, symbol: &str) -> ExchangeResult<MarketData>;
41
42 async fn get_market_data_batch(&self, symbols: &[String]) -> ExchangeResult<Vec<MarketData>> {
44 let mut results = Vec::with_capacity(symbols.len());
45 for symbol in symbols {
46 results.push(self.get_market_data(symbol).await?);
47 }
48 Ok(results)
49 }
50
51 async fn get_order_book(&self, symbol: &str, depth: u32) -> ExchangeResult<OrderBook>;
53
54 async fn place_order(&self, account_id: &str, order: Order) -> ExchangeResult<Order>;
56
57 async fn cancel_order(&self, account_id: &str, order_id: &str) -> ExchangeResult<Order>;
59
60 async fn get_order(&self, account_id: &str, order_id: &str) -> ExchangeResult<Order>;
62
63 async fn get_active_orders(&self, account_id: &str) -> ExchangeResult<Vec<Order>>;
65
66 async fn is_market_open(&self, symbol: &str) -> ExchangeResult<bool> {
68 let data = self.get_market_data(symbol).await?;
69 Ok(data.market_status.is_tradeable())
70 }
71
72 async fn market_buy(&self, account_id: &str, symbol: &str, lots: u32) -> ExchangeResult<Order> {
74 let order = Order::market(
75 generate_order_id(),
76 symbol,
77 self.id(),
78 OrderDirection::Buy,
79 lots,
80 );
81 self.place_order(account_id, order).await
82 }
83
84 async fn market_sell(
86 &self,
87 account_id: &str,
88 symbol: &str,
89 lots: u32,
90 ) -> ExchangeResult<Order> {
91 let order = Order::market(
92 generate_order_id(),
93 symbol,
94 self.id(),
95 OrderDirection::Sell,
96 lots,
97 );
98 self.place_order(account_id, order).await
99 }
100}
101
102fn generate_order_id() -> String {
104 use std::sync::atomic::{AtomicU64, Ordering};
105 use std::time::{SystemTime, UNIX_EPOCH};
106
107 static COUNTER: AtomicU64 = AtomicU64::new(0);
109
110 let timestamp = SystemTime::now()
111 .duration_since(UNIX_EPOCH)
112 .unwrap_or_default()
113 .as_nanos();
114 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
115 format!("ORD-{timestamp}-{counter}")
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_order_id_generation() {
124 let id1 = generate_order_id();
125 let id2 = generate_order_id();
126
127 assert!(id1.starts_with("ORD-"));
128 assert!(id2.starts_with("ORD-"));
129 assert_ne!(id1, id2);
131 }
132}