Quellcode durchsuchen

添加bitmart标准层

gepangpang vor 1 Jahr
Ursprung
Commit
1529019e48

+ 13 - 1
global/src/account_info.rs

@@ -19,6 +19,12 @@ pub struct AccountInfo {
     pub bitget_access_key: String,
     pub bitget_secret_key: String,
     pub bitget_pass: String,
+    pub bybit_access_key: String,
+    pub bybit_secret_key: String,
+    pub bybit_pass: String,
+    pub bitmart_access_key: String,
+    pub bitmart_secret_key: String,
+    pub bitmart_memo: String
 }
 
 impl AccountInfo {
@@ -37,6 +43,12 @@ impl AccountInfo {
             bitget_access_key: "".to_string(),
             bitget_secret_key: "".to_string(),
             bitget_pass: "".to_string(),
+            bybit_access_key: "".to_string(),
+            bybit_secret_key: "".to_string(),
+            bybit_pass: "".to_string(),
+            bitmart_access_key: "".to_string(),
+            bitmart_secret_key: "".to_string(),
+            bitmart_memo: "".to_string(),
         }
     }
 }
@@ -51,7 +63,7 @@ pub fn get_account_info(file_path: &str) -> AccountInfo {
             from_str(&contents).unwrap_or(AccountInfo::new())
         }
         Err(_) => {
-            error!("没有获取到账号配置文件!");
+            error!("没有获取到账号配置文件 ! ");
             AccountInfo::new()
         }
     };

+ 1 - 1
standard/Cargo.toml

@@ -12,7 +12,7 @@ tokio = { version = "1.31.0", features = ["full"] }
 async-trait = "0.1.73"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.104"
-rust_decimal = "1.32.0"
+rust_decimal = { version = "1.32.0", features = ["maths"] }
 rust_decimal_macros = "1.32.0"
 chrono = "0.4.30"
 futures = "0.3"

+ 600 - 0
standard/src/bitmart_swap.rs

@@ -0,0 +1,600 @@
+use std::collections::{BTreeMap};
+use exchanges::bitmart_swap_rest::BitMartSwapRest;
+use std::io::{Error, ErrorKind};
+use tokio::sync::mpsc::Sender;
+use std::str::FromStr;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use serde_json::json;
+use serde_json::Value::Null;
+use tokio::spawn;
+use tokio::time::Instant;
+use tracing::{error, info};
+use global::trace_stack::TraceStack;
+use crate::exchange::ExchangeEnum;
+use crate::{Account, Market, Order, OrderCommand, Platform, Position, PositionModeEnum, Ticker, utils};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct BitmartSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: BitMartSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl BitmartSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BitmartSwap {
+        let market = Market::new();
+        let mut bitmart_swap = BitmartSwap {
+            exchange: ExchangeEnum::BitmartSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: BitMartSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        bitmart_swap.market = BitmartSwap::get_market(&mut bitmart_swap).await.unwrap();
+        // 修改持仓模式
+        let mode_result = bitmart_swap.set_dual_mode("", true).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("BitmartSwap:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("BitmartSwap:设置持仓模式失败!{:?}", error)
+            }
+        }
+        // 设置持仓杠杆
+        // let lever_rate_result = bitmart_swap.set_dual_leverage("10").await;
+        // match lever_rate_result {
+        //     Ok(ok) => {
+        //         info!("BitmartSwap:设置持仓杠杆成功!{:?}", ok);
+        //     }
+        //     Err(error) => {
+        //         error!("BitmartSwap:设置持仓杠杆失败!{:?}", error)
+        //     }
+        // }
+
+        return bitmart_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for BitmartSwap {
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+
+    fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::BitmartSwap }
+
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+
+    fn get_self_is_colo(&self) -> bool { self.is_colo }
+
+    fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+
+    fn get_self_market(&self) -> Market { self.market.clone() }
+
+    fn get_request_delays(&self) -> Vec<i64> {
+        // self.request.get_delays()
+        vec![]
+    }
+
+    fn get_request_avg_delay(&self) -> Decimal {
+        // self.request.get_avg_delay()
+        Decimal::ZERO
+    }
+
+    fn get_request_max_delay(&self) -> i64 { 0 }
+
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let response = self.request.get_server_time().await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取服务器时间异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let result = res_data_json["server_time"].to_string();
+        Ok(result)
+    }
+
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        let params = json!({});
+        let response = self.request.get_account(params).await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取账户信息异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let mut account = Account::new();
+        for data in res_data_json.as_array().unwrap() {
+            let currency = data["currency"].as_str().unwrap().to_string();
+            if currency == "USDT" {
+                let position_deposit = Decimal::from_str(data["position_deposit"].as_str().unwrap()).unwrap();
+
+                let available_balance = Decimal::from_str(data["available_balance"].as_str().unwrap()).unwrap();
+                let frozen_balance = Decimal::from_str(data["frozen_balance"].as_str().unwrap()).unwrap();
+                let balance = position_deposit + available_balance + frozen_balance;
+                // 格式化account信息
+                account = Account {
+                    coin: currency,
+                    balance,
+                    available_balance,
+                    frozen_balance,
+                    stocks: Default::default(),
+                    available_stocks: Default::default(),
+                    frozen_stocks: Default::default(),
+                };
+            }
+        }
+        return Ok(account);
+    }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap get_spot_account:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let ct_val = self.market.ct_val;
+        let params = json!({
+            "symbol": symbol_format
+        });
+        let response = self.request.get_user_position(params).await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取仓位异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let positions_info = res_data_json.as_array().unwrap();
+        let result = positions_info.iter().map(|item| { format_position_item(item, &ct_val) }).collect();
+        Ok(result)
+    }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        let params = json!({});
+        let response = self.request.get_user_position(params).await;
+
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取仓位异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let positions_info = res_data_json.as_array().unwrap();
+        let result = positions_info.iter().map(|item| { format_position_item(item, &Decimal::ONE) }).collect();
+        Ok(result)
+    }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        return self.get_ticker_symbol(self.symbol.clone()).await;
+    }
+
+    async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap get_ticker_symbol:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        self.get_market_symbol(self.symbol.clone()).await
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+
+        let params = json!({
+            "symbol": symbol_format,
+        });
+        let response = self.request.get_market(params).await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取市场信息异常{:?}", response).to_string()));
+        }
+        let res_data_json = response.data["symbols"].as_array().unwrap();
+        let market_info = res_data_json[0].clone();
+
+        if !market_info["symbol"].as_str().unwrap().to_string().eq(&symbol_format) {
+            return Err(Error::new(ErrorKind::NotFound, format!("符号未找到:symbol={}, response={:?}", symbol_format, response))).unwrap();
+        }
+
+        let base_asset = market_info["base_currency"].as_str().unwrap().to_string();
+        let quote_asset = market_info["quote_currency"].as_str().unwrap().to_string();
+
+        let tick_size = Decimal::from_str(market_info["price_precision"].as_str().unwrap()).unwrap();
+        let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+        let amount_size = Decimal::from_str(market_info["vol_precision"].as_str().unwrap()).unwrap();
+        let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+        let min_qty = Decimal::from_str(market_info["min_volume"].as_str().unwrap()).unwrap();
+        let max_qty = Decimal::from_str(market_info["max_volume"].as_str().unwrap()).unwrap();
+        let ct_val = Decimal::from_str(market_info["contract_size"].as_str().unwrap()).unwrap();
+
+        let result = Market {
+            symbol: format!("{}_{}", base_asset, quote_asset),
+            base_asset,
+            quote_asset,
+            tick_size,
+            amount_size,
+            price_precision,
+            amount_precision,
+            min_qty,
+            max_qty,
+            min_notional: min_qty * ct_val,
+            max_notional: max_qty * ct_val,
+            ct_val,
+        };
+        Ok(result)
+    }
+
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let ct_val = self.market.ct_val;
+
+        let params = json!({
+            "symbol": symbol_format,
+            "order_id": order_id
+        });
+        if order_id.eq("") { return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 该交易所只能使用订单号查询订单详情").to_string())); };
+        let response = self.request.get_order_details(params).await;
+        if response.code != 200 || response.data == Null {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取订单详情异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let result = format_order_item(res_data_json.clone(), ct_val);
+        Ok(result)
+    }
+
+    async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let ct_val = self.market.ct_val;
+
+        let params = json!({
+            "symbol": symbol_format
+        });
+        let response = self.request.get_orders(params).await;
+        if response.code != 200 || response.data == Null {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 获取订单列表异常{:?}", response).to_string()));
+        }
+        let res_data_json = response.data;
+        let orders = res_data_json.as_array().unwrap();
+        let result = orders.iter().map(|item| format_order_item(item.clone(), ct_val)).collect();
+        Ok(result)
+    }
+
+    async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let ct_val = self.market.ct_val;
+
+        return self.take_order_symbol(self.symbol.clone(), ct_val, custom_id, origin_side, price, amount).await;
+    }
+
+    async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(symbol, "");
+        let final_size = (amount / ct_val).floor();
+        let mut params = json!({
+            "symbol": symbol_format,
+            "client_order_id": custom_id.clone(),
+            "open_type": "cross",
+            "size": Decimal::to_i64(&final_size).unwrap(),
+            "leverage": "10"
+        });
+        if price.eq(&Decimal::ZERO) {
+            params["size"] = json!(Decimal::to_i64(&amount).unwrap());
+            params["type"] = json!("market");
+        } else {
+            params["price"] = json!(price.to_string());
+            params["type"] = json!("limit");
+        };
+        match origin_side {
+            "kd" => {
+                params["side"] = json!(1);
+            }
+            "pd" => {
+                params["side"] = json!(3);
+            }
+            "kk" => {
+                params["side"] = json!(4);
+            }
+            "pk" => {
+                params["side"] = json!(2);
+            }
+            _ => { panic!("bitmart_usdt_swap 下单参数错误"); }
+        };
+        
+        let response = self.request.swap_order(params).await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 下单异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let result = Order {
+            id: res_data_json["order_id"].to_string(),
+            custom_id: custom_id.to_string(),
+            price: Decimal::ZERO,
+            amount: Decimal::ZERO,
+            deal_amount: Decimal::ZERO,
+            avg_price: Decimal::ZERO,
+            status: "NEW".to_string(),
+            order_type: "".to_string(),
+            trace_stack: TraceStack::new(0, Instant::now()).on_special("323 bitmart_swap".to_string()),
+        };
+        return Ok(result);
+    }
+
+    async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+            "order_id": order_id
+        });
+        let response = self.request.cancel_order(params).await;
+
+        if order_id == "" && custom_id != "" { return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 该交易所不支持自定义订单号取消订单").to_string())); }
+        // 取消失败,进行报错
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 取消订单异常{:?}", response).to_string()));
+        }
+
+        let res_data_json = response.data;
+        let mut id = order_id.to_string();
+        let mut custom_id = custom_id.to_string();
+        let result = Order {
+            id,
+            custom_id,
+            price: Decimal::ZERO,
+            amount: Decimal::ZERO,
+            deal_amount: Decimal::ZERO,
+            avg_price: Decimal::ZERO,
+            status: "REMOVE".to_string(),
+            order_type: "".to_string(),
+            trace_stack: TraceStack::new(0, Instant::now()).on_special("354 bitmart_swap".to_string()),
+        };
+        Ok(result)
+    }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+        });
+        let response = self.request.cancel_price_order(params).await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 撤销所有订单异常{:?}", response).to_string()));
+        }
+
+        Ok(vec![])
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+        });
+        let response = self.request.cancel_price_order(params).await;
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitmart_swap 撤销所有订单异常{:?}", response).to_string()));
+        }
+
+        Ok(vec![])
+    }
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<serde_json::Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap take_stop_loss_order:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<serde_json::Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap cancel_stop_loss_order:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap set_dual_mode:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+            "lever_rate": leverage,
+            "open_type": "cross"
+        });
+        let response = self.request.setting_dual_leverage(params).await;
+
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::Other, format!("设置杠杆失败:{:?}", response).to_string()));
+        }
+
+        return Ok(response.data.to_string());
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap set_auto_deposit_status:该交易所方法未实现".to_string()))
+    }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitmart_swap wallet_transfers:该交易所方法未实现".to_string()))
+    }
+
+    async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
+        let mut handles = vec![];
+
+        // 下单指令
+        for item in order_command.limits_open.keys() {
+            let mut ts = trace_stack.clone();
+
+            let amount = Decimal::from_str(&*order_command.limits_open[item].get(0).unwrap().clone()).unwrap();
+            let side = order_command.limits_open[item].get(1).unwrap().clone();
+            let price = Decimal::from_str(&*order_command.limits_open[item].get(2).unwrap().clone()).unwrap();
+            let cid = order_command.limits_open[item].get(3).unwrap().clone();
+
+            //  order_name: [数量,方向,价格,c_id]
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                // TraceStack::show_delay(&ts.ins);
+                ts.on_before_send();
+                let result = self_clone.take_order(cid.as_str(), side.as_str(), price, amount).await;
+                ts.on_after_send();
+
+                match result {
+                    Ok(mut result) => {
+                        result.trace_stack = ts;
+
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        info!(?error);
+                        let mut err_order = Order::new();
+                        err_order.custom_id = cid.clone();
+                        err_order.status = "REMOVE".to_string();
+
+                        self_clone.order_sender.send(err_order).await.unwrap();
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(handles);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 撤销订单
+        let mut cancel_handlers = vec![];
+        for item in order_command.cancel.keys() {
+            let order_id = order_command.cancel[item].get(1).unwrap().clone();
+            let custom_id = order_command.cancel[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                let result = self_clone.cancel_order(&order_id, &custom_id).await;
+                match result {
+                    Ok(_) => {
+                        // result_sd.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        // 取消失败去查订单。
+                        let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+                        match query_rst {
+                            Ok(order) => {
+                                self_clone.order_sender.send(order).await.unwrap();
+                            }
+                            Err(err) => {
+                                error!("撤单失败,而且查单也失败了,bitmart_swap,oid={}, cid={}, err={:?}。", order_id.clone(), custom_id.clone(), err);
+                            }
+                        }
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            cancel_handlers.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(cancel_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 检查订单指令
+        let mut check_handlers = vec![];
+        for item in order_command.check.keys() {
+            let order_id = order_command.check[item].get(1).unwrap().clone();
+            let custom_id = order_command.check[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                let result = self_clone.get_order_detail(order_id.as_str(), custom_id.as_str()).await;
+                match result {
+                    Ok(result) => {
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            check_handlers.push(handle)
+        }
+
+        let futures = FuturesUnordered::from_iter(check_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+    }
+}
+
+// pub fn format_account_info(balance_data: Value) -> Account {
+//     let balance_coin = balance_data["coin"].as_str().unwrap().to_string().to_uppercase();
+//     let available_balance = Decimal::from_str(balance_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_balance = Decimal::from_str(balance_data["frozen"].as_str().unwrap()).unwrap();
+//     let balance = available_balance + frozen_balance;
+//
+//     Account {
+//         coin: balance_coin,
+//         balance,
+//         available_balance,
+//         frozen_balance,
+//         stocks: Decimal::ZERO,
+//         available_stocks: Decimal::ZERO,
+//         frozen_stocks: Decimal::ZERO,
+//     }
+// }
+
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+    let id = order["order_id"].as_str().unwrap().to_string();
+    let custom_id = order["client_order_id"].as_str().unwrap().to_string();
+    let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+    let amount = Decimal::from_str(order["size"].as_str().unwrap()).unwrap() * ct_val;
+    let deal_amount = Decimal::from_str(order["deal_size"].as_str().unwrap()).unwrap() * ct_val;
+    let avg_price = Decimal::from_str(order["deal_avg_price"].as_str().unwrap()).unwrap() * ct_val;
+
+    let status = order["state"].to_string();
+
+    let custom_status = if ["4"].contains(&&*status) {
+        "REMOVE".to_string()
+    } else if ["1", "2"].contains(&&*status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id,
+        custom_id,
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["type"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("567 bitmart_swap".to_string()),
+    }
+}
+
+pub fn format_position_item(position: &serde_json::Value, ct_val: &Decimal) -> Position {
+    let symbol = position["symbol"].as_str().unwrap().to_string();
+    let margin_level = Decimal::from_str(&position["leverage"].as_str().unwrap()).unwrap();
+    let amount = Decimal::from_str(position["current_amount"].as_str().unwrap()).unwrap() * ct_val;
+
+    let frozen_amount = Decimal::from_str(position["close_vol"].as_str().unwrap()).unwrap();
+    let price = Decimal::from_str(position["entry_price"].as_str().unwrap()).unwrap();
+    let profit = Decimal::from_str(position["unrealized_value"].as_str().unwrap()).unwrap();
+    let position_mode = match position["position_type"].to_string().as_str() {
+        "1" => PositionModeEnum::Long,
+        "2" => PositionModeEnum::Short,
+        _ => {
+            panic!("bitmart_usdt_swap: 未知的持仓模式: {}", position["position_mode"].as_str().unwrap())
+        }
+    };
+    let margin = Decimal::from_str(position["maintenance_margin"].as_str().unwrap()).unwrap();
+
+    Position {
+        symbol,
+        margin_level,
+        amount,
+        frozen_amount,
+        price,
+        profit,
+        position_mode,
+        margin,
+    }
+}

+ 146 - 0
standard/src/bitmart_swap_handle.rs

@@ -0,0 +1,146 @@
+use std::str::FromStr;
+use chrono::Utc;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tokio::time::Instant;
+use tracing::trace;
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
+
+// 处理账号信息
+pub fn handle_account_info(response: &ResponseData, _symbol: &String) -> Account {
+    let account_info = response.data.clone();
+
+    let position_deposit = Decimal::from_str(account_info["position_deposit"].as_str().unwrap()).unwrap();
+
+    let available_balance = Decimal::from_str(account_info["available_balance"].as_str().unwrap()).unwrap();
+    let frozen_balance = Decimal::from_str(account_info["frozen_balance"].as_str().unwrap()).unwrap();
+    let balance = position_deposit + available_balance + frozen_balance;
+    // 格式化account信息
+    let result = Account {
+        coin: account_info["currency"].as_str().unwrap().to_string(),
+        balance,
+        available_balance,
+        frozen_balance,
+        stocks: Default::default(),
+        available_stocks: Default::default(),
+        frozen_stocks: Default::default(),
+    };
+    return result;
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+    let res_data_json = res_data.data;
+    let order_info = vec![format_order_item(res_data_json.clone(), ct_val)];
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+// 处理订单信息
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+    let id = order["order_id"].as_str().unwrap().to_string();
+    let custom_id = order["client_order_id"].as_str().unwrap().to_string();
+    let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+    let amount = Decimal::from_str(order["size"].as_str().unwrap()).unwrap() * ct_val;
+    let deal_amount = Decimal::from_str(order["deal_size"].as_str().unwrap()).unwrap() * ct_val;
+    let avg_price = Decimal::from_str(order["deal_avg_price"].as_str().unwrap()).unwrap() * ct_val;
+
+    let status = order["state"].to_string();
+
+    let custom_status = if ["4"].contains(&&*status) {
+        "REMOVE".to_string()
+    } else if ["1", "2"].contains(&&*status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id,
+        custom_id,
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["order_price_type"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("68 bitmart_swap_handle".to_string()),
+    }
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value["price"].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value["vol"].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+// 格式化Ticker信息
+pub fn handle_ticker(res_data: &ResponseData) -> SpecialDepth {
+    let bp = Decimal::from_str((*res_data).data["bid_price"].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_str((*res_data).data["bid_vol"].as_str().unwrap()).unwrap();
+    let ap = Decimal::from_str((*res_data).data["ask_price"].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_str((*res_data).data["ask_vol"].as_str().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+
+
+    let t = Decimal::from_i64(Utc::now().timestamp_millis()).unwrap();
+    let create_at = Utc::now().timestamp_millis() * 1000;
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+    let depth_info = vec![bp, bq, ap, aq];
+
+    SpecialDepth {
+        name: (*res_data).label.clone(),
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+        create_at,
+    }
+}
+
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
+    let res_data_json = res_data.data.as_array().unwrap();
+    res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect()
+}
+
+pub fn format_position_item(position: &serde_json::Value, ct_val: &Decimal) -> Position {
+    let open_avg_price = Decimal::from_str(position["open_avg_price"].as_str().unwrap()).unwrap();
+
+    let symbol = position["symbol"].as_str().unwrap().to_string();
+    let margin_level = dec!(-1);
+    let amount = Decimal::from_str(position["hold_volume"].as_str().unwrap()).unwrap() * ct_val;
+    let frozen_amount = Decimal::from_str(position["frozen_volume"].as_str().unwrap()).unwrap();
+    let price = Decimal::from_str(position["hold_avg_price"].as_str().unwrap()).unwrap();
+    let profit = amount * price - open_avg_price * amount;
+    let position_mode = match position["position_type"].to_string().as_str() {
+        "1" => PositionModeEnum::Long,
+        "2" => PositionModeEnum::Short,
+        _ => {
+            panic!("bitmart_usdt_swap: 未知的持仓模式: {}", position["position_mode"].as_str().unwrap())
+        }
+    };
+    let margin = amount * open_avg_price;
+
+    Position {
+        symbol,
+        margin_level,
+        amount,
+        frozen_amount,
+        price,
+        profit,
+        position_mode,
+        margin,
+    }
+}

+ 6 - 1
standard/src/exchange.rs

@@ -10,6 +10,7 @@ use crate::kucoin_swap::KucoinSwap;
 // use crate::bitget_spot::BitgetSpot;
 use crate::bybit_swap::BybitSwap;
 use crate::bitget_swap::BitgetSwap;
+use crate::bitmart_swap::BitmartSwap;
 use crate::coinex_swap::CoinexSwap;
 // use crate::kucoin_spot::KucoinSpot;
 // use crate::okx_swap::OkxSwap;
@@ -32,7 +33,8 @@ pub enum ExchangeEnum {
     // BitgetSpot,
     CoinexSwap,
     BitgetSwap,
-    BybitSwap
+    BybitSwap,
+    BitmartSwap
 }
 
 /// Exchange结构体
@@ -107,6 +109,9 @@ impl Exchange {
             ExchangeEnum::CoinexSwap => {
                 Box::new(CoinexSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
+            ExchangeEnum::BitmartSwap => {
+                Box::new(BitmartSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
         }
     }
 }

+ 30 - 9
standard/src/handle_info.rs

@@ -3,11 +3,12 @@ use std::str::FromStr;
 use rust_decimal::{Decimal};
 use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
-use tracing::{error, info};
+use serde_json::json;
+use tracing::{error, info, trace};
 use exchanges::response_base::ResponseData;
 use global::public_params;
 use crate::exchange::ExchangeEnum;
-use crate::{binance_swap_handle, gate_swap_handle, bybit_swap_handle, bitget_swap_handle, kucoin_handle, coinex_swap_handle};
+use crate::{binance_swap_handle, gate_swap_handle, bybit_swap_handle, bitget_swap_handle, kucoin_handle, coinex_swap_handle, bitmart_swap_handle};
 use crate::{Account, MarketOrder, Position, SpecialDepth, SpecialOrder, SpecialTicker};
 
 #[allow(dead_code)]
@@ -17,7 +18,7 @@ pub struct DepthParam {
     pub depth_asks: Vec<MarketOrder>,
     pub depth_bids: Vec<MarketOrder>,
     pub t: Decimal,
-    pub create_at: i64
+    pub create_at: i64,
 }
 
 #[allow(dead_code)]
@@ -46,13 +47,16 @@ impl HandleSwapInfo {
             // },
             ExchangeEnum::BitgetSwap => {
                 bitget_swap_handle::handle_account_info(res_data, symbol)
-            },
+            }
             ExchangeEnum::BybitSwap => {
                 bybit_swap_handle::handle_account_info(res_data, symbol)
             }
             ExchangeEnum::CoinexSwap => {
                 coinex_swap_handle::handle_account_info(res_data, symbol)
             }
+            ExchangeEnum::BitmartSwap => {
+                bitget_swap_handle::handle_account_info(res_data, symbol)
+            }
             _ => {
                 error!("未找到该交易所!handle_account_info: {:?}",exchange);
                 panic!("未找到该交易所!handle_account_info: {:?}", exchange);
@@ -90,13 +94,16 @@ impl HandleSwapInfo {
                 info!(?res_data);
                 panic!("BitgetSwap 85 未实现格式化");
                 // bitget_swap_handle::handle_special_ticker(res_data)
-            },
+            }
             ExchangeEnum::BybitSwap => {
                 bybit_swap_handle::handle_ticker(res_data)
             }
             ExchangeEnum::CoinexSwap => {
                 coinex_swap_handle::handle_ticker(res_data)
             }
+            ExchangeEnum::BitmartSwap => {
+                bitmart_swap_handle::handle_ticker(res_data)
+            }
         }
     }
     // 处理position信息
@@ -125,13 +132,16 @@ impl HandleSwapInfo {
             // },
             ExchangeEnum::BitgetSwap => {
                 bitget_swap_handle::handle_position(res_data, ct_val)
-            },
+            }
             ExchangeEnum::BybitSwap => {
                 bybit_swap_handle::handle_position(res_data, ct_val)
             }
             ExchangeEnum::CoinexSwap => {
                 coinex_swap_handle::handle_position(res_data, ct_val)
             }
+            ExchangeEnum::BitmartSwap => {
+                bitmart_swap_handle::handle_position(res_data, ct_val)
+            }
         }
     }
     // 处理订单信息
@@ -158,13 +168,16 @@ impl HandleSwapInfo {
             // },
             ExchangeEnum::BitgetSwap => {
                 bitget_swap_handle::handle_order(res_data, ct_val)
-            },
+            }
             ExchangeEnum::BybitSwap => {
                 bybit_swap_handle::handle_order(res_data, ct_val)
             }
             ExchangeEnum::CoinexSwap => {
                 coinex_swap_handle::handle_order(res_data, ct_val)
             }
+            ExchangeEnum::BitmartSwap => {
+                bitmart_swap_handle::handle_order(res_data, ct_val)
+            }
         }
     }
 
@@ -304,7 +317,7 @@ pub fn format_depth(exchange: ExchangeEnum, res_data: &ResponseData) -> DepthPar
             depth_bids = bybit_swap_handle::format_depth_items(res_data.data["b"].clone());
             t = Decimal::from_i64(res_data.reach_time).unwrap();
             create_at = res_data.reach_time * 1000;
-        },
+        }
         ExchangeEnum::CoinexSwap => {
             let depth = &res_data.data["depth"];
             depth_asks = coinex_swap_handle::format_depth_items(&depth["asks"].clone());
@@ -313,13 +326,21 @@ pub fn format_depth(exchange: ExchangeEnum, res_data: &ResponseData) -> DepthPar
             t = Decimal::from_i64(time).unwrap();
             create_at = time * 1000;
         }
+        ExchangeEnum::BitmartSwap => {
+            let depth = res_data.data["depths"].clone();
+            let way = res_data.data["way"].as_i64().unwrap();
+            depth_asks = bitmart_swap_handle::format_depth_items(if way == 2 { depth.clone() } else { json!([]) });
+            depth_bids = bitmart_swap_handle::format_depth_items(if way == 1 { depth.clone() } else { json!([]) });
+            t = Decimal::from_i64(res_data.data["ms_t"].as_i64().unwrap()).unwrap();
+            create_at = res_data.data["ms_t"].as_i64().unwrap() * 1000;
+        }
     }
 
     DepthParam {
         depth_asks,
         depth_bids,
         t,
-        create_at
+        create_at,
     }
 }
 

+ 2 - 0
standard/src/lib.rs

@@ -38,6 +38,8 @@ mod bitget_swap;
 mod bitget_swap_handle;
 mod coinex_swap;
 mod coinex_swap_handle;
+mod bitmart_swap;
+pub mod bitmart_swap_handle;
 
 /// 持仓模式枚举
 /// - `Both`:单持仓方向

+ 68 - 0
standard/tests/bitmart_handle_test.rs

@@ -0,0 +1,68 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::bitmart_swap_ws::BitMartSwapSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::{test_new_exchange_wss};
+
+const SYMBOL: &str = "XRP_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_subscribe_type = vec![
+        BitMartSwapSubscribeType::PuFuturesDepth
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitmartSwap, SYMBOL, bitmart_subscribe_type, "depth").await;
+}
+
+// 测试订阅Account信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_account() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_subscribe_type = vec![
+        BitMartSwapSubscribeType::PrFuturesBalances
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitmartSwap, SYMBOL, bitmart_subscribe_type, "account").await;
+}
+
+// 测试订阅Position信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_position() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_subscribe_type = vec![
+        BitMartSwapSubscribeType::PrFuturesPositions
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitmartSwap, SYMBOL, bitmart_subscribe_type, "position").await;
+}
+
+// 测试订阅Ticker信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_subscribe_type = vec![
+        BitMartSwapSubscribeType::PuFuturesTicker
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitmartSwap, SYMBOL, bitmart_subscribe_type, "ticker").await;
+}
+
+// 测试订阅Orders信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_subscribe_type = vec![
+        BitMartSwapSubscribeType::PrFuturesOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitmartSwap, SYMBOL, bitmart_subscribe_type, "orders").await;
+}

+ 282 - 0
standard/tests/bitmart_swap_test.rs

@@ -0,0 +1,282 @@
+mod exchange_test;
+
+use std::collections::BTreeMap;
+use std::env;
+use std::io::Error;
+use chrono::Utc;
+use rust_decimal_macros::dec;
+use tokio::sync::mpsc;
+use tokio::time::Instant;
+use tracing::{instrument, trace};
+use global::trace_stack::TraceStack;
+use standard::exchange::{Exchange, ExchangeEnum};
+use standard::{Order, OrderCommand, Platform, utils};
+use crate::exchange_test::{test_new_exchange};
+
+const SYMBOL: &str = "XRP_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_self_exchange = bitmart_swap_exchange.get_self_exchange();
+    trace!(?bitmart_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_self_symbol = bitmart_swap_exchange.get_self_symbol();
+    trace!(?bitmart_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_self_is_colo = bitmart_swap_exchange.get_self_is_colo();
+    trace!(?bitmart_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_self_params = bitmart_swap_exchange.get_self_params();
+    trace!("bitmart_get_self_params={:?}",bitmart_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_self_market = bitmart_swap_exchange.get_self_market();
+    trace!(?bitmart_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_request_delays = bitmart_swap_exchange.get_request_delays();
+    trace!(?bitmart_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_request_avg_delay = bitmart_swap_exchange.get_request_avg_delay();
+    trace!(?bitmart_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_request_max_delay = bitmart_swap_exchange.get_request_max_delay();
+    trace!(?bitmart_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_server_time = bitmart_swap_exchange.get_server_time().await;
+    trace!(?bitmart_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_account = bitmart_swap_exchange.get_account().await;
+    trace!(?bitmart_get_account);
+}
+
+// 测试获取持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_position() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_position = bitmart_swap_exchange.get_position().await;
+    trace!(?bitmart_get_position);
+}
+
+// 测试获取所有持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_positions() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_positions = bitmart_swap_exchange.get_positions().await;
+    trace!(?bitmart_get_positions);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_ticker = bitmart_swap_exchange.get_ticker().await;
+    trace!(?bitmart_get_ticker);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_market() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_market = bitmart_swap_exchange.get_market().await;
+    trace!(?bitmart_get_market);
+}
+
+// 测试获取Order详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_order_detail() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_order_detail = bitmart_swap_exchange.get_order_detail("2405178193088178", "CCC999999919").await;
+    trace!(?bitmart_get_order_detail);
+}
+
+// 测试获取Order列表信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_orders_list() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_get_orders_list = bitmart_swap_exchange.get_orders_list("finished").await;
+    trace!(?bitmart_get_orders_list);
+}
+
+// 测试下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_take_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_take_order = bitmart_swap_exchange.take_order("CCC999999919", "kd", dec!(0.5150), dec!(10)).await;
+    trace!(?bitmart_take_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_cancel_order = bitmart_swap_exchange.cancel_order("2405178191502884", "CCC999999918").await;
+    trace!(?bitmart_cancel_order);
+}
+
+// 测试批量撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_cancel_orders = bitmart_swap_exchange.cancel_orders_all().await;
+    trace!(?bitmart_cancel_orders);
+}
+
+// 测试设置持仓模式
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_dual_mode() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_set_dual_mode = bitmart_swap_exchange.set_dual_mode("usdt", true).await;
+    trace!(?bitmart_set_dual_mode);
+}
+
+// 测试设置杠杆
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_dual_leverage() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitmartSwap, SYMBOL).await;
+    let bitmart_set_dual_leverage = bitmart_swap_exchange.set_dual_leverage("10").await;
+    trace!(?bitmart_set_dual_leverage);
+}
+
+// 测试指令下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_command_order() {
+    global::log_utils::init_log_with_trace();
+    utils::proxy_handle();
+
+    let (order_sender, mut order_receiver): (mpsc::Sender<Order>, mpsc::Receiver<Order>) = mpsc::channel(1024);
+    let (error_sender, mut error_receiver): (mpsc::Sender<Error>, mpsc::Receiver<Error>) = mpsc::channel(1024);
+
+    let mut params: BTreeMap<String, String> = BTreeMap::new();
+    let access_key = env::var("bitmart_access_key").unwrap_or("".to_string());
+    let secret_key = env::var("bitmart_secret_key").unwrap_or("".to_string());
+    params.insert("access_key".to_string(), access_key);
+    params.insert("secret_key".to_string(), secret_key);
+
+    let mut bitmart_swap_exchange: Box<dyn Platform> = Exchange::new(ExchangeEnum::BitmartSwap, SYMBOL.to_string(), false, params, order_sender, error_sender).await;
+
+    let mut command = OrderCommand::new();
+    command.cancel.insert("888888".to_string(), vec!["888888".to_string(), "".to_string()]);
+    command.limits_open.insert("888888".to_string(), vec!["100".to_string(), "kd".to_string(), "0.18".to_string(), "888888".to_string()]);
+    command.limits_close.insert("999999".to_string(), vec!["100".to_string(), "kk".to_string(), "0.25".to_string(), "999999".to_string()]);
+    command.check.insert("888888".to_string(), vec!["999999".to_string(), "".to_string()]);
+    bitmart_swap_exchange.command_order(&mut command, &TraceStack::new(Utc::now().timestamp_micros(), Instant::now())).await;
+
+    loop {
+        if let Ok(order) = order_receiver.try_recv() {
+            trace!(?order);
+        }
+        if let Ok(error) = error_receiver.try_recv() {
+            trace!(?error);
+        }
+    }
+}

+ 432 - 329
standard/tests/exchange_test.rs

@@ -2,11 +2,9 @@ use std::collections::{BTreeMap};
 use std::io::{Error};
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use futures::StreamExt;
 use rust_decimal_macros::dec;
 use tokio::sync::mpsc::{channel, Receiver, Sender};
 use tokio::sync::Mutex;
-use tokio::try_join;
 use tracing::{error, trace};
 // use exchanges::binance_spot_ws::{BinanceSpotLogin, BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
 // use exchanges::binance_swap_ws::{BinanceSwapLogin, BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
@@ -14,16 +12,18 @@ use tracing::{error, trace};
 // use exchanges::kucoin_spot_ws::{KucoinSpotLogin, KucoinSpotSubscribeType, KucoinSpotWs, KucoinSpotWsType};
 // use exchanges::gate_swap_ws::{GateSwapLogin, GateSwapSubscribeType, GateSwapWs, GateSwapWsType};
 // use exchanges::bitget_spot_ws::{BitgetSpotLogin, BitgetSpotSubscribeType, BitgetSpotWs, BitgetSpotWsType};
-use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+// use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+use exchanges::bitmart_swap_ws::{BitMartSwapLogin, BitMartSwapSubscribeType, BitMartSwapWs, BitMartSwapWsType};
 use exchanges::response_base::ResponseData;
 use standard::exchange::{Exchange, ExchangeEnum};
 // use standard::{binance_spot_handle, Order, Platform, utils};
 // use standard::{binance_handle, Order, Platform, utils};
 // use standard::{kucoin_handle, Order, Platform, utils};
 // use standard::{kucoin_spot_handle, Order, Platform, utils};
-// use standard::{gate_handle, Order, Platform, utils};
+// use standard::{gate_swap_handle, handle_info, Order, Platform, utils};
 // use standard::{bitget_spot_handle, Order, Platform, utils};
-use standard::{okx_handle, Order, Platform, utils};
+// use standard::{okx_handle, Order, Platform, utils};
+use standard::{bitmart_swap_handle, handle_info, Order, Platform, utils};
 
 // 创建实体
 #[allow(dead_code)]
@@ -42,14 +42,14 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
             params.insert("secret_key".to_string(), secret_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
-        ExchangeEnum::BinanceSpot => {
-            let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = account_info.binance_access_key;
-            let secret_key = account_info.binance_secret_key;
-            params.insert("access_key".to_string(), access_key);
-            params.insert("secret_key".to_string(), secret_key);
-            Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
-        }
+        // ExchangeEnum::BinanceSpot => {
+        //     let mut params: BTreeMap<String, String> = BTreeMap::new();
+        //     let access_key = account_info.binance_access_key;
+        //     let secret_key = account_info.binance_secret_key;
+        //     params.insert("access_key".to_string(), access_key);
+        //     params.insert("secret_key".to_string(), secret_key);
+        //     Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        // }
         ExchangeEnum::GateSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
             let access_key = account_info.gate_access_key;
@@ -58,14 +58,14 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
             params.insert("secret_key".to_string(), secret_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
-        ExchangeEnum::GateSpot => {
-            let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = account_info.gate_access_key;
-            let secret_key = account_info.gate_secret_key;
-            params.insert("access_key".to_string(), access_key);
-            params.insert("secret_key".to_string(), secret_key);
-            Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
-        }
+        // ExchangeEnum::GateSpot => {
+        //     let mut params: BTreeMap<String, String> = BTreeMap::new();
+        //     let access_key = account_info.gate_access_key;
+        //     let secret_key = account_info.gate_secret_key;
+        //     params.insert("access_key".to_string(), access_key);
+        //     params.insert("secret_key".to_string(), secret_key);
+        //     Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        // }
         ExchangeEnum::KucoinSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
             let access_key = account_info.kucoin_access_key;
@@ -76,99 +76,132 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
             params.insert("pass_key".to_string(), pass_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
-        ExchangeEnum::KucoinSpot => {
+        // ExchangeEnum::KucoinSpot => {
+        //     let mut params: BTreeMap<String, String> = BTreeMap::new();
+        //     let access_key = account_info.kucoin_access_key;
+        //     let secret_key = account_info.kucoin_secret_key;
+        //     let pass_key = account_info.kucoin_pass;
+        //     params.insert("access_key".to_string(), access_key);
+        //     params.insert("secret_key".to_string(), secret_key);
+        //     params.insert("pass_key".to_string(), pass_key);
+        //     Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        // }
+        // ExchangeEnum::OkxSwap => {
+        //     let mut params: BTreeMap<String, String> = BTreeMap::new();
+        //     let access_key = account_info.okx_access_key;
+        //     let secret_key = account_info.okx_secret_key;
+        //     let pass_key = account_info.okx_pass;
+        //     params.insert("access_key".to_string(), access_key);
+        //     params.insert("secret_key".to_string(), secret_key);
+        //     params.insert("pass_key".to_string(), pass_key);
+        //     Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        // }
+        // ExchangeEnum::BitgetSpot => {
+        //     let mut params: BTreeMap<String, String> = BTreeMap::new();
+        //     let access_key = account_info.bitget_access_key;
+        //     let secret_key = account_info.bitget_secret_key;
+        //     let pass_key = account_info.bitget_pass;
+        //     params.insert("access_key".to_string(), access_key);
+        //     params.insert("secret_key".to_string(), secret_key);
+        //     params.insert("pass_key".to_string(), pass_key);
+        //     Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        // }
+        ExchangeEnum::BitgetSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = account_info.kucoin_access_key;
-            let secret_key = account_info.kucoin_secret_key;
-            let pass_key = account_info.kucoin_pass;
+            let access_key = account_info.bitget_access_key;
+            let secret_key = account_info.bitget_secret_key;
+            let pass_key = account_info.bitget_pass;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             params.insert("pass_key".to_string(), pass_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
-        ExchangeEnum::OkxSwap => {
+        ExchangeEnum::BybitSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = account_info.okx_access_key;
-            let secret_key = account_info.okx_secret_key;
-            let pass_key = account_info.okx_pass;
+            let access_key = account_info.bybit_access_key;
+            let secret_key = account_info.bybit_secret_key;
+            let pass_key = account_info.bybit_pass;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             params.insert("pass_key".to_string(), pass_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
-        ExchangeEnum::BitgetSpot => {
+        ExchangeEnum::BitmartSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = account_info.bitget_access_key;
-            let secret_key = account_info.bitget_secret_key;
-            let pass_key = account_info.bitget_pass;
+            let access_key = account_info.bitmart_access_key;
+            let secret_key = account_info.bitmart_secret_key;
+            let api_memo = account_info.bitmart_memo;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
-            params.insert("pass_key".to_string(), pass_key);
+            params.insert("api_memo".to_string(), api_memo);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
+        _ => {
+            panic!("该交易所未实现!")
+        }
     }
 }
 
 #[allow(dead_code)]
-pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<OkxSwapSubscribeType>: From<T> {
+pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<BitMartSwapSubscribeType>: From<T> {
     utils::proxy_handle();
     let account_info = global::account_info::get_account_info("../test_account.toml");
     match exchange {
-        ExchangeEnum::BinanceSpot => {
-            // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
-            // trace!(symbol_format);
-            // let name = format!("binance_spot@{}", symbol.to_string().to_lowercase());
-            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-            // let write_tx_am = Arc::new(Mutex::new(write_tx));
-            // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
-            //
-            // let params = BinanceSpotLogin {
-            //     api_key: account_info.binance_access_key,
-            //     api_secret: account_info.binance_secret_key,
-            // };
-            // let mut exchange_wss;
-            // exchange_wss = BinanceSpotWs::new_label(name, false, Option::from(params), BinanceSpotWsType::PublicAndPrivate);
-            // exchange_wss.set_symbols(vec![symbol_format]);
-            // exchange_wss.set_subscribe(subscriber_type.into());
-            //
-            //
-            // let mold_arc = Arc::new(mold.to_string());
-            // //读取
-            // tokio::spawn(async move {
-            //     let mold_clone = Arc::clone(&mold_arc);
-            //     loop {
-            //         if let Some(data) = read_rx.next().await {
-            //             trace!("原始数据 data:{:?}",data);
-            //             match mold_clone.as_str() {
-            //                 "depth" => {
-            //                     if data.data != "" {
-            //                         let result = binance_spot_handle::handle_special_depth(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "ticker" => {
-            //                     if data.data != "" {
-            //                         let result = binance_spot_handle::handle_special_ticker(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 _ => {
-            //                     error!("没有该命令!mode={}", mold_clone);
-            //                     panic!("没有该命令!mode={}", mold_clone)
-            //                 }
-            //             }
-            //         }
-            //     };
-            // });
-            //
-            // let t1 = tokio::spawn(async move {
-            //     //链接
-            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-            // });
-            // try_join!(t1).unwrap();
-        }
+        // ExchangeEnum::BinanceSpot => {
+        // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
+        // trace!(symbol_format);
+        // let name = format!("binance_spot@{}", symbol.to_string().to_lowercase());
+        // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+        // let write_tx_am = Arc::new(Mutex::new(write_tx));
+        // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+        //
+        // let params = BinanceSpotLogin {
+        //     api_key: account_info.binance_access_key,
+        //     api_secret: account_info.binance_secret_key,
+        // };
+        // let mut exchange_wss;
+        // exchange_wss = BinanceSpotWs::new_label(name, false, Option::from(params), BinanceSpotWsType::PublicAndPrivate);
+        // exchange_wss.set_symbols(vec![symbol_format]);
+        // exchange_wss.set_subscribe(subscriber_type.into());
+        //
+        //
+        // let mold_arc = Arc::new(mold.to_string());
+        // //读取
+        // tokio::spawn(async move {
+        //     let mold_clone = Arc::clone(&mold_arc);
+        //     loop {
+        //         if let Some(data) = read_rx.next().await {
+        //             trace!("原始数据 data:{:?}",data);
+        //             match mold_clone.as_str() {
+        //                 "depth" => {
+        //                     if data.data != "" {
+        //                         let result = binance_spot_handle::handle_special_depth(data);
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "ticker" => {
+        //                     if data.data != "" {
+        //                         let result = binance_spot_handle::handle_special_ticker(data);
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 _ => {
+        //                     error!("没有该命令!mode={}", mold_clone);
+        //                     panic!("没有该命令!mode={}", mold_clone)
+        //                 }
+        //             }
+        //         }
+        //     };
+        // });
+        //
+        // let t1 = tokio::spawn(async move {
+        //     //链接
+        //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        // });
+        // try_join!(t1).unwrap();
+        // }
         ExchangeEnum::BinanceSwap => {
             // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
             // trace!(symbol_format);
@@ -291,81 +324,80 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             // });
             // try_join!(t1).unwrap();
         }
-        ExchangeEnum::KucoinSpot => {
-            // let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
-            // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
-            // trace!(symbol_format);
-            // let name = format!("kucoin_spot@{}", symbol.to_string().to_lowercase());
-            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-            // let write_tx_am = Arc::new(Mutex::new(write_tx));
-            // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
-            //
-            // let params = KucoinSpotLogin {
-            //     access_key: account_info.kucoin_access_key,
-            //     secret_key: account_info.kucoin_secret_key,
-            //     pass_key: account_info.kucoin_pass,
-            // };
-            // let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
-            //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Public).await
-            // } else {
-            //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Private).await
-            // };
-            // exchange_wss.set_symbols(vec![symbol_format]);
-            // exchange_wss.set_subscribe(subscriber_type.into());
-            //
-            // let mold_arc = Arc::new(mold.to_string());
-            // tokio::spawn(async move {
-            //     let mold_clone = Arc::clone(&mold_arc);
-            //     loop {
-            //         if let Some(data) = read_rx.next().await {
-            //             trace!("原始数据 data:{:?}",data);
-            //             match mold_clone.as_str() {
-            //                 "depth" => {
-            //                     if data.data != "" {
-            //                         let result = kucoin_spot_handle::handle_special_depth(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "ticker" => {
-            //                     if data.data != "" {
-            //                         let result = kucoin_spot_handle::handle_special_ticker(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "account" => {
-            //                     if data.data != "" {
-            //                         let result = kucoin_spot_handle::handle_account_info(data, symbol_back.clone());
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "orders" => {
-            //                     if data.data != "" {
-            //                         let result = kucoin_spot_handle::handle_order(data, dec!(1));
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 _ => {
-            //                     error!("没有该命令!mode={}", mold_clone);
-            //                     panic!("没有该命令!mode={}", mold_clone)
-            //                 }
-            //             }
-            //         }
-            //     }
-            // });
-            // let t1 = tokio::spawn(async move {
-            //     //链接
-            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-            // });
-            // try_join!(t1).unwrap();
-        }
+        // ExchangeEnum::KucoinSpot => {
+        // let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+        // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
+        // trace!(symbol_format);
+        // let name = format!("kucoin_spot@{}", symbol.to_string().to_lowercase());
+        // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+        // let write_tx_am = Arc::new(Mutex::new(write_tx));
+        // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+        //
+        // let params = KucoinSpotLogin {
+        //     access_key: account_info.kucoin_access_key,
+        //     secret_key: account_info.kucoin_secret_key,
+        //     pass_key: account_info.kucoin_pass,
+        // };
+        // let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
+        //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Public).await
+        // } else {
+        //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Private).await
+        // };
+        // exchange_wss.set_symbols(vec![symbol_format]);
+        // exchange_wss.set_subscribe(subscriber_type.into());
+        //
+        // let mold_arc = Arc::new(mold.to_string());
+        // tokio::spawn(async move {
+        //     let mold_clone = Arc::clone(&mold_arc);
+        //     loop {
+        //         if let Some(data) = read_rx.next().await {
+        //             trace!("原始数据 data:{:?}",data);
+        //             match mold_clone.as_str() {
+        //                 "depth" => {
+        //                     if data.data != "" {
+        //                         let result = kucoin_spot_handle::handle_special_depth(data);
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "ticker" => {
+        //                     if data.data != "" {
+        //                         let result = kucoin_spot_handle::handle_special_ticker(data);
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "account" => {
+        //                     if data.data != "" {
+        //                         let result = kucoin_spot_handle::handle_account_info(data, symbol_back.clone());
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "orders" => {
+        //                     if data.data != "" {
+        //                         let result = kucoin_spot_handle::handle_order(data, dec!(1));
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 _ => {
+        //                     error!("没有该命令!mode={}", mold_clone);
+        //                     panic!("没有该命令!mode={}", mold_clone)
+        //                 }
+        //             }
+        //         }
+        //     }
+        // });
+        // let t1 = tokio::spawn(async move {
+        //     //链接
+        //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        // });
+        // try_join!(t1).unwrap();
+        // }
         ExchangeEnum::GateSwap => {
             // let symbol_format = utils::format_symbol(symbol.to_string(), "_").to_uppercase();
             // trace!(symbol_format);
             // let name = format!("gate_swap@{}", symbol.to_string().to_lowercase());
             // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
             // let write_tx_am = Arc::new(Mutex::new(write_tx));
             // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
             //
@@ -377,204 +409,275 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             // exchange_wss.set_symbols(vec![symbol_format.clone()]);
             // exchange_wss.set_subscribe(subscriber_type.into());
             //
-            // let mold_arc = Arc::new(mold.to_string());
-            // tokio::spawn(async move {
-            //     let mold_clone = Arc::clone(&mold_arc);
-            //     loop {
-            //         if let Some(data) = read_rx.next().await {
-            //             trace!("原始数据 data:{:?}",data);
-            //             match mold_clone.as_str() {
-            //                 "depth" => {
-            //                     if data.data != "" {
-            //                         let result = gate_handle::handle_special_depth(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "ticker" => {
-            //                     if data.data != "" {
-            //                         let result = gate_handle::handle_special_ticker(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "account" => {
-            //                     if data.data != "" {
-            //                         let result = gate_handle::handle_account_info(data, symbol_format.clone());
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "orders" => {
-            //                     if data.data != "" {
-            //                         let result = gate_handle::handle_order(data, dec!(1));
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 _ => {
-            //                     error!("没有该命令!mode={}", mold_clone);
-            //                     panic!("没有该命令!mode={}", mold_clone)
-            //                 }
-            //             }
-            //         }
-            //     }
-            // });
-            // let t1 = tokio::spawn(async move {
-            //     //链接
-            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-            // });
-            // try_join!(t1).unwrap();
-        }
-        ExchangeEnum::BitgetSpot => {
-            // let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
-            // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
-            // trace!(symbol_format);
-            // let name = format!("bitget_spot@{}", symbol.to_string().to_lowercase());
-            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-            // let write_tx_am = Arc::new(Mutex::new(write_tx));
-            // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
-            //
-            // let params = BitgetSpotLogin {
-            //     api_key: account_info.bitget_access_key,
-            //     secret_key: account_info.bitget_secret_key,
-            //     passphrase_key: account_info.bitget_pass,
-            // };
-            //
-            // let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
-            //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Public)
-            // } else {
-            //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Private)
-            // };
-            // exchange_wss.set_symbols(vec![symbol_format]);
-            // exchange_wss.set_subscribe(subscriber_type.into());
+            // let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+            // let mold_clone = mold.to_string().clone();
+            // let fun = move |data: ResponseData| {
+            //     let symbol_format_c = symbol_format.clone();
+            //     let mold_cc = mold_clone.clone();
             //
-            // let mold_arc = Arc::new(mold.to_string());
-            // //读取
-            // tokio::spawn(async move {
-            //     loop {
-            //         let mold_clone = Arc::clone(&mold_arc);
-            //         if let Some(data) = read_rx.next().await {
-            //             trace!("原始数据 data:{:?}",data);
-            //             match mold_clone.as_str() {
-            //                 "depth" => {
-            //                     if data.data != "" {
-            //                         let result = bitget_spot_handle::handle_special_depth(data);
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "ticker" => {
-            //                     if data.data != "" {
-            //                         let result = bitget_spot_handle::handle_special_ticker(data);
-            //                         trace!(?result)
-            //                     }
+            //     async move {
+            //         trace!("原始数据 data:{:?}",data);
+            //         match mold_cc.as_str() {
+            //             "depth" => {
+            //                 if data.data != "" {
+            //                     let result = handle_info::format_depth(ExchangeEnum::GateSwap, &data);
+            //                     trace!(?result)
             //                 }
-            //                 "account" => {
-            //                     if data.data != "" {
-            //                         let result = bitget_spot_handle::handle_account_info(data, symbol_back.clone());
-            //                         trace!(?result)
-            //                     }
+            //             }
+            //             "ticker" => {
+            //                 if data.data != "" {
+            //                     let result = gate_swap_handle::handle_book_ticker(&data);
+            //                     trace!(?result)
             //                 }
-            //                 "orders" => {
-            //                     if data.data != "" {
-            //                         let result = bitget_spot_handle::handle_order(data, dec!(1));
-            //                         trace!(?result)
-            //                     }
+            //             }
+            //             "account" => {
+            //                 if data.data != "" {
+            //                     let result = gate_swap_handle::handle_account_info(&data, &symbol_format_c);
+            //                     trace!(?result)
             //                 }
-            //                 _ => {
-            //                     error!("没有该命令!mode={}", mold_clone);
-            //                     panic!("没有该命令!mode={}", mold_clone)
+            //             }
+            //             "orders" => {
+            //                 if data.data != "" {
+            //                     let result = gate_swap_handle::handle_order(data, dec!(1));
+            //                     trace!(?result)
             //                 }
             //             }
-            //         }
+            //             _ => {
+            //                 error!("没有该命令!mode={}", mold_cc);
+            //                 panic!("没有该命令!mode={}", mold_cc)
+            //             }
+            //         };
             //     }
-            // });
-            // let t1 = tokio::spawn(async move {
-            //     //链接
-            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-            // });
-            // try_join!(t1).unwrap();
+            // };
+            // exchange_wss.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
         }
-        ExchangeEnum::OkxSwap => {
-            let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+        // ExchangeEnum::BitgetSpot => {
+        // let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+        // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
+        // trace!(symbol_format);
+        // let name = format!("bitget_spot@{}", symbol.to_string().to_lowercase());
+        // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+        // let write_tx_am = Arc::new(Mutex::new(write_tx));
+        // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+        //
+        // let params = BitgetSpotLogin {
+        //     api_key: account_info.bitget_access_key,
+        //     secret_key: account_info.bitget_secret_key,
+        //     passphrase_key: account_info.bitget_pass,
+        // };
+        //
+        // let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
+        //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Public)
+        // } else {
+        //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Private)
+        // };
+        // exchange_wss.set_symbols(vec![symbol_format]);
+        // exchange_wss.set_subscribe(subscriber_type.into());
+        //
+        // let mold_arc = Arc::new(mold.to_string());
+        // //读取
+        // tokio::spawn(async move {
+        //     loop {
+        //         let mold_clone = Arc::clone(&mold_arc);
+        //         if let Some(data) = read_rx.next().await {
+        //             trace!("原始数据 data:{:?}",data);
+        //             match mold_clone.as_str() {
+        //                 "depth" => {
+        //                     if data.data != "" {
+        //                         let result = bitget_spot_handle::handle_special_depth(data);
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "ticker" => {
+        //                     if data.data != "" {
+        //                         let result = bitget_spot_handle::handle_special_ticker(data);
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "account" => {
+        //                     if data.data != "" {
+        //                         let result = bitget_spot_handle::handle_account_info(data, symbol_back.clone());
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 "orders" => {
+        //                     if data.data != "" {
+        //                         let result = bitget_spot_handle::handle_order(data, dec!(1));
+        //                         trace!(?result)
+        //                     }
+        //                 }
+        //                 _ => {
+        //                     error!("没有该命令!mode={}", mold_clone);
+        //                     panic!("没有该命令!mode={}", mold_clone)
+        //                 }
+        //             }
+        //         }
+        //     }
+        // });
+        // let t1 = tokio::spawn(async move {
+        //     //链接
+        //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        // });
+        // try_join!(t1).unwrap();
+        // }
+        // ExchangeEnum::OkxSwap => {
+        //     let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+        //     trace!(symbol_format);
+        //     let name = format!("okx_swap@{}", symbol.to_string().to_lowercase());
+        //     let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        //     let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+        //     let write_tx_am = Arc::new(Mutex::new(write_tx));
+        //     let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+        //
+        //     let params = OkxSwapLogin {
+        //         api_key: account_info.okx_access_key,
+        //         secret_key: account_info.okx_secret_key,
+        //         passphrase: account_info.okx_pass,
+        //     };
+        //
+        //     let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
+        //         OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Public)
+        //     } else if ["account", "orders", "position"].contains(&mold) {
+        //         OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Private)
+        //     } else {
+        //         OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Business)
+        //     };
+        //
+        //     exchange_wss.set_symbols(vec![symbol_format.clone()]);
+        //     exchange_wss.set_subscribe(subscriber_type.into());
+        //
+        //     let mold_arc = Arc::new(mold.to_string());
+        //     tokio::spawn(async move {
+        //         let mold_clone = Arc::clone(&mold_arc);
+        //         loop {
+        //             if let Some(data) = read_rx.next().await {
+        //                 trace!("原始数据 data:{:?}",data);
+        //                 match mold_clone.as_str() {
+        //                     "depth" => {
+        //                         if data.data != "" {
+        //                             let result = okx_handle::handle_special_depth(data);
+        //                             trace!(?result)
+        //                         }
+        //                     }
+        //                     "ticker" => {
+        //                         if data.data != "" {
+        //                             let result = okx_handle::handle_special_ticker(data);
+        //                             trace!(?result)
+        //                         }
+        //                     }
+        //                     "account" => {
+        //                         if data.data != "" {
+        //                             let result = okx_handle::handle_account_info(data, symbol_format.clone());
+        //                             trace!(?result)
+        //                         }
+        //                     }
+        //                     "position" => {
+        //                         if data.data != "" {
+        //                             let result = okx_handle::handle_position(data, dec!(10));
+        //                             trace!(?result)
+        //                         }
+        //                     }
+        //                     "orders" => {
+        //                         if data.data != "" {
+        //                             let result = okx_handle::handle_order(data, dec!(10));
+        //                             trace!(?result)
+        //                         }
+        //                     }
+        //                     _ => {
+        //                         error!("没有该命令!mode={}", mold_clone);
+        //                         panic!("没有该命令!mode={}", mold_clone)
+        //                     }
+        //                 }
+        //             }
+        //         }
+        //     });
+        //
+        //     let t1 = tokio::spawn(async move {
+        //         //链接
+        //         let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        //         exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        //     });
+        //     try_join!(t1).unwrap();
+        // }
+        ExchangeEnum::BitmartSwap => {
+            let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
             trace!(symbol_format);
-            let name = format!("okx_swap@{}", symbol.to_string().to_lowercase());
+            let name = format!("bitmart_swap@{}", symbol.to_string().to_lowercase());
             let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-            let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
             let write_tx_am = Arc::new(Mutex::new(write_tx));
             let is_shutdown_arc = Arc::new(AtomicBool::new(true));
 
-            let params = OkxSwapLogin {
-                api_key: account_info.okx_access_key,
-                secret_key: account_info.okx_secret_key,
-                passphrase: account_info.okx_pass,
+            let params = BitMartSwapLogin {
+                api_key: account_info.bitmart_access_key,
+                secret: account_info.bitmart_secret_key,
+                api_memo: account_info.bitmart_memo
             };
 
-            let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
-                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Public)
-            } else if ["account", "orders", "position"].contains(&mold) {
-                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Private)
-            } else {
-                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Business)
+            let bitmart_wss_type = match mold.to_string().clone().as_str() {
+                "depth" => BitMartSwapWsType::Public,
+                _ => BitMartSwapWsType::Private
             };
 
+            let mut exchange_wss = BitMartSwapWs::new_label(name, Option::from(params), bitmart_wss_type);
             exchange_wss.set_symbols(vec![symbol_format.clone()]);
             exchange_wss.set_subscribe(subscriber_type.into());
+            let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+            let mold_clone = mold.to_string().clone();
+            let fun = move |data: ResponseData| {
+                let symbol_format_c = symbol_format.clone();
+                let mold_cc = mold_clone.clone();
 
-            let mold_arc = Arc::new(mold.to_string());
-            tokio::spawn(async move {
-                let mold_clone = Arc::clone(&mold_arc);
-                loop {
-                    if let Some(data) = read_rx.next().await {
-                        trace!("原始数据 data:{:?}",data);
-                        match mold_clone.as_str() {
-                            "depth" => {
-                                if data.data != "" {
-                                    let result = okx_handle::handle_special_depth(data);
-                                    trace!(?result)
-                                }
-                            }
-                            "ticker" => {
-                                if data.data != "" {
-                                    let result = okx_handle::handle_special_ticker(data);
-                                    trace!(?result)
-                                }
+                async move {
+                    trace!("原始数据 data:{:?}",data);
+                    match mold_cc.as_str() {
+                        "depth" => {
+                            if data.data != "" {
+                                let result = handle_info::format_depth(ExchangeEnum::BitmartSwap, &data);
+                                trace!("-------------------------------");
                             }
-                            "account" => {
-                                if data.data != "" {
-                                    let result = okx_handle::handle_account_info(data, symbol_format.clone());
-                                    trace!(?result)
-                                }
+                        }
+                        "position" => {
+                            if data.data != "" {
+                                let result = bitmart_swap_handle::handle_position(&data, &dec!(10));
+                                trace!("-------------------------------");
+                                trace!(?result)
                             }
-                            "position" => {
-                                if data.data != "" {
-                                    let result = okx_handle::handle_position(data, dec!(10));
-                                    trace!(?result)
-                                }
+                        }
+                        "ticker" => {
+                            if data.data != "" {
+                                let result = bitmart_swap_handle::handle_ticker(&data);
+                                trace!("-------------------------------");
+                                trace!(?result)
                             }
-                            "orders" => {
-                                if data.data != "" {
-                                    let result = okx_handle::handle_order(data, dec!(10));
-                                    trace!(?result)
-                                }
+                        }
+                        "account" => {
+                            if data.data != "" {
+                                let result = bitmart_swap_handle::handle_account_info(&data, &symbol_format_c);
+                                trace!("-------------------------------");
+                                trace!(?result)
                             }
-                            _ => {
-                                error!("没有该命令!mode={}", mold_clone);
-                                panic!("没有该命令!mode={}", mold_clone)
+                        }
+                        "orders" => {
+                            println!("{:?}", data);
+                            if data.data != "" {
+                                let result = bitmart_swap_handle::handle_order(data, dec!(10));
+                                trace!("-------------------------------");
+                                trace!(?result)
                             }
                         }
-                    }
+                        _ => {
+                            error!("没有该命令!mode={}", mold_cc);
+                            panic!("没有该命令!mode={}", mold_cc)
+                        }
+                    };
                 }
-            });
-
-            let t1 = tokio::spawn(async move {
-                //链接
-                let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-                exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-            });
-            try_join!(t1).unwrap();
+            };
+            exchange_wss.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
         }
         _ => {
-            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+            error!("该交易所不支持!test_new_exchange_wss:{:?}", exchange);
             panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
         }
     }