Browse Source

1.添加订阅数据部分
2.添加每分钟计算msv部分

DESKTOP-NE65RNK\Citrus_limon 1 year ago
parent
commit
1de36d1d97
51 changed files with 8595 additions and 2 deletions
  1. 19 0
      Cargo.toml
  2. 3 0
      exchanges/.gitignore
  3. 52 0
      exchanges/Cargo.toml
  4. 32 0
      exchanges/README.md
  5. 614 0
      exchanges/src/gate_swap_rest.rs
  6. 365 0
      exchanges/src/gate_swap_ws.rs
  7. 102 0
      exchanges/src/http_tool.rs
  8. 7 0
      exchanges/src/lib.rs
  9. 110 0
      exchanges/src/proxy.rs
  10. 50 0
      exchanges/src/response_base.rs
  11. 439 0
      exchanges/src/socket_tool.rs
  12. 9 0
      exchanges/src/utils.rs
  13. 94 0
      exchanges/tests/binance_spot_test.rs
  14. 246 0
      exchanges/tests/binance_swap_test.rs
  15. 129 0
      exchanges/tests/bingx_swap_test.rs
  16. 515 0
      exchanges/tests/bitget_spot_test.rs
  17. 145 0
      exchanges/tests/bitget_swap_test.rs
  18. 320 0
      exchanges/tests/bybit_swap_test.rs
  19. 139 0
      exchanges/tests/coinsph_swap_test.rs
  20. 132 0
      exchanges/tests/cointr_swap_test.rs
  21. 86 0
      exchanges/tests/crypto_spot_test.rs
  22. 181 0
      exchanges/tests/gate_swap_test.rs
  23. 264 0
      exchanges/tests/kucoin_spot_test.rs
  24. 168 0
      exchanges/tests/kucoin_swap_test.rs
  25. 128 0
      exchanges/tests/mexc_swap_test.rs
  26. 127 0
      exchanges/tests/okx_swap_test.rs
  27. 67 0
      exchanges/tests/socket_tool_test.rs
  28. 547 0
      exchanges/tests/test.rs
  29. 140 0
      exchanges/tests/woo_swap_test.rs
  30. 5 0
      global/.gitignore
  31. 25 0
      global/Cargo.toml
  32. 1 0
      global/src/lib.rs
  33. 105 0
      global/src/log_utils.rs
  34. 12 0
      src/control_c.rs
  35. 109 0
      src/gate_usdt_swap_data_listener.rs
  36. 185 0
      src/json_db_utils.rs
  37. 36 0
      src/listener_tools.rs
  38. 43 2
      src/main.rs
  39. 368 0
      src/msv.rs
  40. 140 0
      src/server.rs
  41. 5 0
      standard/.gitignore
  42. 22 0
      standard/Cargo.toml
  43. 69 0
      standard/src/exchange.rs
  44. 19 0
      standard/src/exchange_struct_handler.rs
  45. 645 0
      standard/src/gate_swap.rs
  46. 22 0
      standard/src/gate_swap_handle.rs
  47. 586 0
      standard/src/lib.rs
  48. 16 0
      standard/src/utils.rs
  49. 581 0
      standard/tests/exchange_test.rs
  50. 92 0
      standard/tests/gate_handle_test.rs
  51. 279 0
      standard/tests/gate_swap_test.rs

+ 19 - 0
Cargo.toml

@@ -6,3 +6,22 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 
 [dependencies]
 [dependencies]
+standard = { path="./standard" }
+exchanges = { path="./exchanges" }
+global = { path="./global" }
+
+tokio = { version = "1.31.0", features = ["full"] }
+chrono = "0.4.26"
+tracing = "0.1"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+tracing-appender-timezone = { git = "https://github.com/skyfffire/tracing-appender-timezone.git" }
+serde = { version = "1.0.188", features = ["derive"] }
+actix-rt = "2.5.0"
+actix-web = "4.0.0-beta.12"
+ctrlc = "3.2.5"
+serde_json = "1.0.105"
+rust_decimal = { version = "1.32.0", features = ["maths"] }
+rust_decimal_macros = "1.32.0"
+futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
+futures-channel = "0.3.28"
+lazy_static = "1.4.0"

+ 3 - 0
exchanges/.gitignore

@@ -0,0 +1,3 @@
+/target
+/.idea
+/log

+ 52 - 0
exchanges/Cargo.toml

@@ -0,0 +1,52 @@
+[package]
+name = "exchanges"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+# json
+serde_json = "1.0.104"
+#tungstenite = { git = "https://github.com/PrivateRookie/tungstenite-rs.git", rev = "1d9289276518e5ab7e5194126d40b441d8938375" }
+#tungstenite = { git = "https://github.com/PrivateRookie/tungstenite-rs.git", rev = "f368f3087d50d97658fda5337550e587bb1ba1b6" }
+
+tokio-tungstenite= { git = "https://github.com/HonestHouLiang/tokio-tungstenite.git",rev = "208fc9b09bcc2e2c8cb52e1cde5087446464fc91"  }
+futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
+futures-channel = "0.3.28"
+
+url = "2.4.0"
+base64 = "0.13"
+tokio = { version = "1.31.0", features = ["full"] }
+chrono = "0.4.26"
+hex = "0.4"
+reqwest = { version = "0.11.14", features = ["json"] }
+
+
+ring = "0.16.20"
+data-encoding = "2.4.0"
+
+
+hmac = "0.8.1"
+sha2 = "0.9.8"
+#tokio-tungstenite = "0.14"
+
+##代替f64避免精度丢失
+rust_decimal = "1.32.0"
+rust_decimal_macros = "1.32.0"
+
+
+##日志
+global = { path="../global" }
+tracing = "0.1"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+
+##生成 xlsx
+rust_xlsxwriter = "0.58.0"
+once_cell = "1.19.0"
+flate2 = "1.0.28"
+
+# 火币需要的一个加密包
+percent-encoding = "2.3.1"
+
+

+ 32 - 0
exchanges/README.md

@@ -0,0 +1,32 @@
+## 声明
+
+
+## 项目结构解析
+
+```
+|
+├─ main                                 // 系统入口
+│
+├─ exchanges                            // 交易所层(网络层)
+│
+├─ standard                             // 标准化层(中间件)
+│
+└─ strategy                             // 策略层(主逻辑、风控等)
+```
+
+```
+币安深度示例-
+ //币安---深度socket-公共频道订阅
+    let get_res_data = move |res_data: ResponseData| {
+        async move {
+            println!("?????{:?}", res_data);
+        }
+    };
+ 
+
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("lable".parse().unwrap(), "binance".parse().unwrap());//交易行名称
+
+    let ba_exc = BinanceUsdtSwapWs::new(false, true, btree_map);
+    ba_exc.kline(vec![&"BTCUSDT"], get_res_data);
+```

+ 614 - 0
exchanges/src/gate_swap_rest.rs

@@ -0,0 +1,614 @@
+use std::collections::BTreeMap;
+use reqwest::header::HeaderMap;
+use ring::{digest};
+use hex;
+use hmac::{Hmac, Mac, NewMac};
+use reqwest::Client;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use sha2::Sha512;
+use tracing::{error, info};
+
+#[derive(Clone)]
+pub struct GateSwapRest {
+    tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl GateSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> GateSwapRest
+    {
+        return GateSwapRest::new_with_tag("default-GateSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> GateSwapRest
+    {
+        let base_url = if is_colo {
+            let url = "https://apiv4-private.gateapi.io".to_string();
+            info!("开启高速通道:{:?}",url);
+            url
+        } else {
+            let url = "https://api.gateio.ws".to_string();
+            info!("走普通通道:{}",url);
+            url
+        };
+
+
+        if is_colo {} else {}
+        /*****返回结构体*******/
+        GateSwapRest {
+            tag,
+            base_url: base_url.to_string(),
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //获取服务器当前时间
+    pub async fn get_server_time(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/spot/time"),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询个人交易费率
+    pub async fn wallet_fee(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/wallet/fee"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询合约账户
+    pub async fn get_account(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/accounts", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //用户仓位列表
+    pub async fn get_user_position(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/positions", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //双仓模式下的持仓信息
+    pub async fn get_position(&mut self, settle: String, contract: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/dual_comp/positions/{}", settle, contract),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取所有合约交易行情统计
+    pub async fn get_ticker(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/tickers", settle),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询所有的合约信息
+    pub async fn get_market_details(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/contracts", settle),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询单个订单详情
+    pub async fn get_order_details(&mut self, settle: String, order_id: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders/{}", settle, order_id),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询合约订单列表
+    pub async fn get_orders(&mut self, settle: String, status: String) -> ResponseData {
+        let params = serde_json::json!({
+            "status":status
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //下单:side-下单方向,pos_side-持仓方向
+    pub async fn order(&mut self,
+                       settle: String,
+                       pos_side: String,
+                       side: String,
+                       contract: String,
+                       size: i64,
+                       price: String,
+                       text: String,
+    ) -> ResponseData
+    {
+        if side != "buy" && side != "sell" {
+            ResponseData::error(self.tag.clone(), format!("未知下单方向!{}", side));
+        }
+        if pos_side != "long" && pos_side != "short" {
+            ResponseData::error(self.tag.clone(), format!("未知持仓方向!{}", side));
+        }
+        let mut param = serde_json::json!({
+            "contract":contract, //合约标识
+            "size":size,
+            "price":price,
+            "text":text,
+        });
+        if price == "0" {
+            param.as_object_mut().unwrap().insert("tif".to_string(), serde_json::json!("ioc"));
+        }
+        if size == 0 {   //数量为0则平仓
+            param.as_object_mut().unwrap().insert("close".to_string(), serde_json::json!(true));
+        }
+        match format!("{}_{}", pos_side, side).as_str() {
+            "long_buy" => {//开多
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(false));
+            }
+            "long_sell" => {//平多
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(true));
+            }
+            "short_buy" => {//平空
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(true));
+            }
+            "short_sell" => {//开空
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(false));
+            }
+            _ => {} // 处理未知请求类型
+        };
+        // trace!("----param{}", param.to_string());
+        let data = self.swap_order(settle, param).await;
+        data
+    }
+    //合约交易下单
+    pub async fn swap_order(&mut self, settle: String, params: serde_json::Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    // 提交一个自动订单
+    pub async fn place_price_order(&mut self, settle: String, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/api/v4".to_string(),
+                     format!("/futures/{}/price_orders", settle),
+                     true,
+                     params.to_string()).await
+    }
+    // 撤销自动订单
+    pub async fn cancel_price_order(&mut self, settle: String, order_id: String) -> ResponseData {
+        self.request("DELETE".to_string(),
+                     "/api/v4".to_string(),
+                     format!("/futures/{}/price_orders/{}", settle, order_id),
+                     true,
+                     "{}".to_string(),
+        ).await
+    }
+    //设置持仓模式
+    pub async fn setting_dual_mode(&mut self, settle: String, dual_mode: bool) -> ResponseData {
+        let params = serde_json::json!({
+                "dual_mode":dual_mode,
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/dual_mode", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //更新双仓模式下的杠杆
+    pub async fn setting_dual_leverage(&mut self, settle: String, symbol: String, leverage: String) -> ResponseData {
+        let params = serde_json::json!({
+                "leverage":leverage,
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/dual_comp/positions/{}/leverage", settle, symbol),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //交易账户互转
+    pub async fn wallet_transfers(&mut self, currency: String, from: String, to: String, amount: String, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+                "currency":currency,
+                "from":from,
+                "to":to,
+                "amount":amount,
+                "settle":settle,
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/wallet/transfers"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //撤销单个订单
+    pub async fn cancel_order(&mut self, settle: String, order_id: String) -> ResponseData {
+        let params = serde_json::json!({
+             });
+
+        let data = self.request("DELETE".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders/{}", settle, order_id),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //撤销所有挂单
+    pub async fn cancel_order_all(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                format!("/sprd/mass-cancel"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //弃用
+    pub async fn swap_bazaar_order(&mut self, text: String, origin_side: String, settle: String, contract: String, size: i64) -> ResponseData {
+        let mut reduce_only = false;
+        let mut param = serde_json::json!({
+            "text":text,
+            "contract":contract,
+            "price":"0",
+            "size":size,
+        });
+
+        let req = match origin_side.as_str() {
+            "kd" => {
+                reduce_only = false;
+                true
+            }
+            "pd" => {
+                reduce_only = true;
+                true
+            }
+            "kk" => {
+                reduce_only = false;
+                true
+            }
+            "pk" => {
+                reduce_only = true;
+                true
+            }
+            _ => { false } // 处理未知请求类型
+        };
+        if req {
+            param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(reduce_only));
+        }
+
+        let data = self.swap_order(settle, param).await;
+        data
+    }
+
+    //批量取消状态为 open 的订单
+    pub async fn cancel_orders(&mut self, settle: String, contract: String) -> ResponseData {
+        let params = serde_json::json!({
+            "contract":contract
+             });
+
+        let data = self.request("DELETE".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查询个人成交记录
+    pub async fn my_trades(&mut self, settle: String, contract: String, limit: i64) -> ResponseData {
+        let mut params = serde_json::json!({
+            "contract":contract,
+            "limit":1000
+        });
+        if limit > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
+
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/my_trades", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查询合约账户变更历史
+    pub async fn account_book(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+                "limit":200
+             });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/account_book", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    pub fn get_delays(&self) -> Vec<i64> {
+        self.delays.clone()
+    }
+    pub fn get_avg_delay(&self) -> Decimal {
+        self.avg_delay.clone()
+    }
+    pub fn get_max_delay(&self) -> i64 {
+        self.max_delay.clone()
+    }
+    fn get_delay_info(&mut self) {
+        let last_100 = if self.delays.len() > 100 {
+            self.delays[self.delays.len() - 100..].to_vec()
+        } else {
+            self.delays.clone()
+        };
+
+        let max_value = last_100.iter().max().unwrap();
+        if max_value.clone().to_owned() > self.max_delay {
+            self.max_delay = max_value.clone().to_owned();
+        }
+
+        let sum: i64 = last_100.iter().sum();
+        let sum_v = Decimal::from_i64(sum).unwrap();
+        let len_v = Decimal::from_u64(last_100.len() as u64).unwrap();
+        self.avg_delay = (sum_v / len_v).round_dp(1);
+        self.delays = last_100.clone().into_iter().collect();
+    }
+
+    //调用请求
+    async fn request(&mut self,
+                     requesst_type: String,
+                     prefix_url: String,
+                     request_url: String,
+                     is_login: bool,
+                     params: String) -> ResponseData
+    {
+        // trace!("login_param:{:?}", self.login_param);
+        //解析账号信息
+        let mut access_key = "".to_string();
+        let mut secret_key = "".to_string();
+        if self.login_param.contains_key("access_key") {
+            access_key = self.login_param.get("access_key").unwrap().to_string();
+        }
+        if self.login_param.contains_key("secret_key") {
+            secret_key = self.login_param.get("secret_key").unwrap().to_string();
+        }
+        let mut is_login_param = true;
+        if access_key == "" || secret_key == "" {
+            is_login_param = false
+        }
+
+        //请求头配置-如果需要登录则存在额外配置
+        let mut body = "".to_string();
+        let timestamp = chrono::Utc::now().timestamp().to_string();
+
+        let mut headers = HeaderMap::new();
+        if requesst_type == "GET" {
+            headers.insert("Content-type", "application/x-www-form-urlencoded".parse().unwrap());
+            headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ".parse().unwrap());
+        } else {
+            headers.insert("Accept", "application/json".parse().unwrap());
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+        }
+
+        if requesst_type == "POST" {
+            body = params.clone();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {//需要登录-且登录参数齐全
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      requesst_type.clone(),
+                                      prefix_url.clone(),
+                                      request_url.clone(),
+                                      params.clone(),
+                                      body.clone(),
+                                      timestamp.clone(),
+                );
+                // trace!("sing:{}", sing);
+                //组装header
+                headers.extend(Self::headers(access_key, timestamp, sing));
+            }
+        }
+
+        // trace!("headers:{:?}", headers);
+        let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response = self.http_tool(
+            base_url.clone(),
+            requesst_type.to_string(),
+            params.clone(),
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+    }
+
+    pub fn headers(access_key: String, timestamp: String, sign: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("KEY", access_key.clone().parse().unwrap());
+        headers.insert("Timestamp", timestamp.clone().parse().unwrap());
+        headers.insert("SIGN", sign.clone().parse().unwrap());
+        headers
+    }
+    pub fn sign(secret_key: String,
+                requesst_type: String, prefix_url: String, request_url: String,
+                params: String, body_data: String, timestamp: String) -> String
+    {
+        let url = format!("{}{}", prefix_url, request_url);
+        let params_str = RestTool::parse_params_to_str(params);
+        let body = Some(body_data);
+        let hashed_payload = if let Some(body) = body {
+            let mut m = digest::Context::new(&digest::SHA512);
+            m.update(body.as_bytes());
+            hex::encode(m.finish().as_ref())
+        } else {
+            String::new()
+        };
+        // trace!("hashed_payload:{}", hashed_payload);
+
+        let message = format!("{}\n{}\n{}\n{}\n{}",
+                              requesst_type,
+                              url,
+                              params_str,
+                              hashed_payload,
+                              timestamp);
+        // trace!("**********", );
+        // trace!("组装数据:{}", message);
+        // trace!("**********", );
+
+        let mut mac = Hmac::<Sha512>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+        mac.update(message.as_bytes());
+        let result = mac.finalize().into_bytes();
+        let sign = hex::encode(result);
+        sign
+    }
+
+
+    async fn http_tool(&mut self, request_path: String, request_type: String, params: String, headers: HeaderMap) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let addrs_url = format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()));
+
+        let request_builder = match request_type.as_str() {
+            "GET" => self.client.get(addrs_url.clone()).headers(headers),
+            "POST" => self.client.post(addrs_url.clone()).body(params.clone()).headers(headers),
+            "DELETE" => self.client.delete(addrs_url.clone()).headers(headers),
+            // "PUT" => self.client.put(url.clone()).json(&params),
+            _ => {
+                panic!("{}", format!("错误的请求类型:{}", request_type.clone()))
+            }
+        };
+
+        // 读取响应的内容
+        let response = request_builder.send().await.unwrap();
+        let is_success = response.status().is_success(); // 先检查状态码
+        let text = response.text().await.unwrap();
+        return if is_success {
+            self.on_success_data(&text)
+        } else {
+            self.on_error_data(&text, &addrs_url, &params)
+        }
+    }
+
+    pub fn on_success_data(&mut self, text: &String) -> ResponseData {
+        let data = serde_json::from_str(text.as_str()).unwrap();
+
+        ResponseData::new(self.tag.clone(), 200, "success".to_string(), data)
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 365 - 0
exchanges/src/gate_swap_ws.rs

@@ -0,0 +1,365 @@
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+use chrono::Utc;
+
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use hex;
+use hmac::{Hmac, Mac, NewMac};
+use rust_decimal::prelude::ToPrimitive;
+use serde_json::{json, Value};
+use sha2::Sha512;
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+//类型
+pub enum GateSwapWsType {
+    PublicAndPrivate(String),
+}
+
+
+//订阅频道
+#[derive(Clone)]
+pub enum GateSwapSubscribeType {
+    PuFuturesOrderBook,
+    PuFuturesCandlesticks,
+    PuFuturesTrades,
+    PuFuturesBookTicker,
+
+    PrFuturesOrders(String),
+    PrFuturesPositions(String),
+    PrFuturesBalances(String),
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct GateSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+}
+
+
+#[derive(Clone)]
+pub struct GateSwapWs {
+    //类型
+    tag: String,
+    //地址
+    address_url: String,
+    //账号信息
+    login_param: Option<GateSwapLogin>,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<GateSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl GateSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<GateSwapLogin>, ws_type: GateSwapWsType) -> GateSwapWs {
+        return GateSwapWs::new_with_tag("default-GateSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: Option<GateSwapLogin>, ws_type: GateSwapWsType) -> GateSwapWs
+    {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            GateSwapWsType::PublicAndPrivate(name) => {
+                if is_colo {
+                    let url = format!("wss://fxws-private.gateapi.io/v4/ws/{}", name.to_string());
+                    info!("开启高速通道:{:?}",url);
+                    url
+                } else {
+                    let url = format!("wss://fx-ws.gateio.ws/v4/ws/{}", name.to_string());
+                    info!("走普通通道:{}",url);
+                    url
+                }
+            }
+        };
+
+
+        GateSwapWs {
+            tag,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 10,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<GateSwapSubscribeType>) {
+        self.subscribe_types.extend(subscribe_types);
+    }
+    //手动添加币对
+    pub fn set_symbols(&mut self, mut b_array: Vec<String>) {
+        for symbol in b_array.iter_mut() {
+            // 大写
+            *symbol = symbol.to_uppercase();
+            // 字符串替换
+            *symbol = symbol.replace("-", "_");
+        }
+        self.symbol_s = b_array;
+    }
+    //频道是否需要登录
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                GateSwapSubscribeType::PuFuturesOrderBook => false,
+                GateSwapSubscribeType::PuFuturesCandlesticks => false,
+                GateSwapSubscribeType::PuFuturesTrades => false,
+                GateSwapSubscribeType::PuFuturesBookTicker => false,
+
+                GateSwapSubscribeType::PrFuturesOrders(_) => true,
+                GateSwapSubscribeType::PrFuturesPositions(_) => true,
+                GateSwapSubscribeType::PrFuturesBalances(_) => true,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: GateSwapSubscribeType, login_param: Option<GateSwapLogin>) -> Value {
+        let time = chrono::Utc::now().timestamp();
+        let mut access_key = "".to_string();
+        let mut secret_key = "".to_string();
+        match login_param {
+            None => {}
+            Some(param) => {
+                access_key = param.api_key.clone();
+                secret_key = param.secret.clone();
+            }
+        }
+
+        match subscribe_type {
+            GateSwapSubscribeType::PuFuturesOrderBook => {
+                json!({
+                    "time": time,
+                    "channel": "futures.order_book",
+                    "event": "subscribe",
+                    "payload": [symbol, "20", "0"]
+                })
+            }
+            GateSwapSubscribeType::PuFuturesBookTicker => {
+                json!({
+                    "time": time,
+                    "channel": "futures.book_ticker",
+                    "event": "subscribe",
+                    "payload": [symbol]
+                })
+            }
+            GateSwapSubscribeType::PuFuturesCandlesticks => {
+                json!({
+                    "time": time,
+                    "channel": "futures.candlesticks",
+                    "event": "subscribe",
+                    "payload":  ["1m", symbol]
+                })
+            }
+            GateSwapSubscribeType::PrFuturesOrders(user_id) => {
+                json!({
+                    "time": time,
+                    "channel": "futures.orders",
+                    "event": "subscribe",
+                    "payload": [user_id, symbol],
+                    "auth": {
+                        "method": "api_key",
+                        "KEY": access_key,
+                        "SIGN":Self::sign(secret_key.to_string(),
+                              "futures.orders".to_string(),
+                              "subscribe".to_string(),
+                              time.to_string())
+                    }
+                })
+            }
+            GateSwapSubscribeType::PrFuturesPositions(user_id) => {
+                json!({
+                    "time": time,
+                    "channel": "futures.positions",
+                    "event": "subscribe",
+                    "payload": [user_id, symbol],
+                    "auth": {
+                        "method": "api_key",
+                        "KEY": access_key,
+                        "SIGN":Self::sign(secret_key.to_string(),
+                              "futures.positions".to_string(),
+                              "subscribe".to_string(),
+                              time.to_string())
+                    }
+                })
+            }
+            GateSwapSubscribeType::PrFuturesBalances(user_id) => {
+                json!({
+                    "time": time,
+                    "channel": "futures.balances",
+                    "event": "subscribe",
+                    "payload": [user_id],
+                    "auth": {
+                        "method": "api_key",
+                        "KEY": access_key,
+                        "SIGN":Self::sign(secret_key.to_string(),
+                              "futures.balances".to_string(),
+                              "subscribe".to_string(),
+                              time.to_string())
+                    }
+                })
+            }
+            GateSwapSubscribeType::PuFuturesTrades => {
+                json!({
+                    "time": time,
+                    "channel": "futures.trades",
+                    "event": "subscribe",
+                    "payload": [symbol]
+                })
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<Value> {
+        let mut args = vec![];
+        for symbol in &self.symbol_s {
+            for subscribe_type in &self.subscribe_types {
+                let ty_str = Self::enum_to_string(symbol.clone(),
+                                                  subscribe_type.clone(),
+                                                  self.login_param.clone(),
+                );
+                args.push(ty_str);
+            }
+        }
+        args
+    }
+    //生成签名
+    fn sign(secret_key: String, channel: String, event: String, time: String) -> String {
+        let message = format!("channel={}&event={}&time={}", channel, event, time);
+        let mut mac = Hmac::<Sha512>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+        mac.update(message.as_bytes());
+        let result = mac.finalize().into_bytes();
+        let sign = hex::encode(result);
+        sign
+    }
+    /*******************************************************************************************************/
+    /*****************************************socket基本*****************************************************/
+    /*******************************************************************************************************/
+    //链接
+    pub async fn ws_connect_async<F, Future>(&mut self,
+                                             is_shutdown_arc: Arc<AtomicBool>,
+                                             handle_function: F,
+                                             write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+                                             write_to_socket_rx: UnboundedReceiver<Message>) -> Result<(), Error>
+        where
+            F: Fn(ResponseData) -> Future + Clone + Send + 'static + Sync,
+            Future: std::future::Future<Output=()> + Send + 'static, // 确保 Fut 是一个 Future,且输出类型为 ()
+    {
+        let login_is = self.contains_pr();
+        let subscription = self.get_subscription();
+        let address_url = self.address_url.clone();
+        let tag = self.tag.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
+        let timestamp = Utc::now().timestamp();
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            let ping_str = json!({
+                "time" : timestamp,
+                "channel" : "futures.ping",
+            });
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Custom(ping_str.to_string()), heartbeat_time).await;
+            trace!("线程-异步心跳-结束");
+        });
+
+        //设置订阅
+        let mut subscribe_array = vec![];
+        if login_is {
+            //登录相关
+        }
+        for s in subscription {
+            subscribe_array.push(s.to_string());
+        }
+
+        //链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            loop {
+                info!("gate_usdt_swap socket 连接中……");
+
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, tag.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong, Self::message_binary).await;
+
+                error!("gate_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text);
+        Option::from(response_data)
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -300, "success".to_string(), Value::Null));
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null));
+    }
+    //数据解析-二进制
+    pub fn message_binary(_po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = format!("Binary:{:?}", _po);
+        Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData
+    {
+        // trace!("原始数据:{}", text);
+        let mut res_data = ResponseData::new("".to_string(), 200, "success".to_string(), Value::Null);
+        let json_value: Value = serde_json::from_str(&text).unwrap();
+
+        if json_value["channel"].as_str() == Option::from("futures.pong") {
+            res_data.code = -301;
+            res_data.message = "success".to_string();
+        } else if json_value.get("error").is_some() {
+            let message = json_value["error"]["message"].as_str().unwrap().to_string();
+            let mes = message.trim_end_matches('\n');
+
+            res_data.code = json_value["error"]["code"].as_i64().unwrap().to_i16().unwrap();
+            res_data.message = mes.to_string();
+        } else if json_value["result"]["status"].as_str() == Option::from("success") {//订阅返回
+            res_data.code = -201;
+            res_data.data = json_value;
+        } else {
+            res_data.channel = format!("{}", json_value["channel"].as_str().unwrap());
+            res_data.code = 200;
+            res_data.data = json_value["result"].clone();
+        }
+        res_data
+    }
+}

+ 102 - 0
exchanges/src/http_tool.rs

@@ -0,0 +1,102 @@
+use tracing::trace;
+use crate::response_base::ResponseData;
+
+#[derive(Clone)]
+pub struct RestTool {
+    pub base_url: String
+}
+
+impl RestTool {
+    pub fn new(base_url: String) -> RestTool {
+        RestTool { base_url }
+    }
+
+    // pub async fn http_tool(&self, request_path: String, request_type: String, params: String, headers: HeaderMap) -> Result<ResponseData, reqwest::Error> {
+    //     let res_data: ResponseData;
+    //     /****请求接口与 地址*/
+    //     let url = format!("{}{}", self.base_url.to_string(), request_path);
+    //     let request_type = request_type.clone().to_uppercase();
+    //     let addrs_url = format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()));
+    //     // let params_json: serde_json::Value = serde_json::from_str(&params).unwrap();
+    //     // trace!("url:{}",url);
+    //     // trace!("addrs_url:{}",url);
+    //     // trace!("params_json:{}",params_json);
+    //     // trace!("headers:{:?}",headers);
+    //
+    //     let req = match request_type.as_str() {
+    //         "GET" => self.client.get(addrs_url.clone()).headers(headers),
+    //         "POST" => self.client.post(addrs_url.clone()).body(params).headers(headers),
+    //         "DELETE" => self.client.delete(addrs_url.clone()).headers(headers),
+    //         // "PUT" => self.client.put(url.clone()).json(&params),
+    //         _ => return Ok(ResponseData::error("".to_string(),format!("错误的请求类型:{}", request_type.clone()))), // 处理未知请求类型
+    //     };
+    //
+    //     let response = req.send().await?;
+    //     if response.status().is_success() {
+    //         // 读取响应的内容
+    //         let body = response.text().await?;
+    //         // trace!("ok-----{}", body);
+    //         res_data = ResponseData::new("".to_string(),"200".to_string(), "success".to_string(), body);
+    //     } else {
+    //         let body = response.text().await?;
+    //         // trace!("error-----{}", body);
+    //         res_data = ResponseData::error("".to_string(),body.to_string())
+    //     }
+    //     Ok(res_data)
+    // }
+
+    //map数据转 get请求参数
+    pub fn parse_params_to_str(parameters: String) -> String {
+        let mut params_str = String::from("");
+        let parsed_json: serde_json::Value = serde_json::from_str(&parameters).unwrap();
+
+        if let Some(json_obj) = parsed_json.as_object() {
+            for (key, value) in json_obj.iter() {
+                let formatted_value = match value {
+                    serde_json::Value::String(s) => s.clone(),
+                    _ => value.to_string()
+                };
+                // trace!("Key: {}", key);
+                // trace!("Value: {}", formatted_value);
+                // let formatted_value = match value {
+                //     Value::String(s) => s.clone(),
+                //     _ => value.to_string()
+                // };
+                let str = format!("{}={}", key, formatted_value);
+                let format_str = format!("{}{}{}", params_str, (if params_str.len() > 0 { "&" } else { "" }), str);
+                params_str = format_str;
+            }
+        }
+        trace!("---json-转字符串拼接:{}",params_str);
+        params_str.to_string()
+    }
+    //res_data 解析
+    pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>) -> ResponseData {
+        match result {
+            Ok(res_data) => {
+                if res_data.code != 0 {
+                    res_data
+                } else {
+                    let json_value= res_data.data;
+
+                    let code = json_value["code"].as_str().unwrap();
+                    let data = serde_json::to_string(&json_value["data"]).unwrap();
+                    let msg = json_value["msg"].as_str().unwrap();
+
+                    // //trace!("--解析成功----code:{}",code);
+                    // //trace!("--解析成功----data:{}",data);
+                    // //trace!("--解析成功----msg:{}",msg);
+                    let success = ResponseData::new("".to_string(),
+                                                    code.parse().unwrap(),
+                                                    msg.parse().unwrap(),
+                                                    data.parse().unwrap());
+                    success
+                }
+            }
+            Err(err) => {
+                let error = ResponseData::error("".to_string(),format!("json 解析失败:{}", err));
+                error
+            }
+        }
+    }
+}

+ 7 - 0
exchanges/src/lib.rs

@@ -0,0 +1,7 @@
+pub mod proxy;
+pub mod response_base;
+pub mod http_tool;
+pub mod socket_tool;
+mod utils;
+pub mod gate_swap_ws;
+pub mod gate_swap_rest;

+ 110 - 0
exchanges/src/proxy.rs

@@ -0,0 +1,110 @@
+use std::env;
+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+use tracing::trace;
+
+
+pub enum ProxyEnum {
+    REST,
+    WS,
+}
+
+pub enum ProxyResponseEnum {
+    NO,
+    YES(SocketAddr),
+}
+
+
+/**代理工具*/
+#[derive(Debug)]
+#[derive(Clone)]
+pub struct ParsingDetail {
+    pub ip_address: String,
+    pub port: String,
+}
+
+impl ParsingDetail {
+    pub fn env_proxy(proxy_enum: ProxyEnum) -> ProxyResponseEnum {
+        let proxy_address = env::var("proxy_address");
+        // 使用std::env::var函数获取环境变量的值,如果返回Err,则说明环境变量不存在
+        let ip_port = match proxy_address {
+            Ok(value) => {
+                trace!("环境变量读取成功:key:proxy_address , val:{}", value);
+                env::set_var("http_proxy", value.to_string());
+                env::set_var("https_proxy", value.to_string());
+                value
+            }
+            Err(_) => {
+                trace!("环境变量读取失败:'proxy_address'");
+                "".to_string()
+            }
+        };
+        if ip_port.len() > 0 {
+            match proxy_enum {
+                ProxyEnum::REST => {
+                    env::set_var("http_proxy", ip_port.to_string());
+                    env::set_var("https_proxy", ip_port.to_string());
+                return     ProxyResponseEnum::NO;
+                }
+                ProxyEnum::WS => {
+                    let ip_port: Vec<&str> = ip_port.split(":").collect();
+                    let ip_array: Vec<&str> = ip_port[0].split(".").collect();
+                    let proxy = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(
+                        ip_array[0].parse().unwrap(),
+                        ip_array[1].parse().unwrap(),
+                        ip_array[2].parse().unwrap(),
+                        ip_array[3].parse().unwrap())
+                    ), ip_port[1].parse().unwrap());
+              return       ProxyResponseEnum::YES(proxy);
+                }
+            }
+        }
+        return  ProxyResponseEnum::NO;
+    }
+
+    fn new(ip_address: String,
+           port: String, ) -> ParsingDetail {
+        ParsingDetail { ip_address, port }
+    }
+    //获取环境变量配置'proxy_address'拿到代理地址
+    pub fn parsing_environment_variables() -> ParsingDetail {
+        let proxy_address = env::var("proxy_address");
+        // 使用std::env::var函数获取环境变量的值,如果返回Err,则说明环境变量不存在
+        match proxy_address {
+            Ok(value) => {
+                //trace!("环境变量读取成功:key:proxy_address , val:{}", value);
+                env::set_var("http_proxy", value.to_string());
+                env::set_var("https_proxy", value.to_string());
+
+                let ip_port: Vec<&str> = value.split(":").collect();
+                let parsing_detail = ParsingDetail::new(ip_port[0].to_string(), ip_port[1].to_string());
+                parsing_detail
+            }
+            Err(_) => {
+                trace!("环境变量读取失败:'proxy_address'");
+                let parsing_detail = ParsingDetail::new("".to_string(), "".to_string());
+                parsing_detail
+            }
+        }
+    }
+
+    //http请求是否开启代理:HTTP 只需要调用该方法即可
+    //原理是 设置全局代理,所以程序如果要走代理只需要执行一次,后续的get,post..都会走代理
+    pub fn http_enable_proxy() -> bool {
+        //拿到环境变量解析的数据
+        let parsing_detail = Self::parsing_environment_variables();
+        if parsing_detail.ip_address.len() > 0 && parsing_detail.port.len() > 0 {
+            let http_proxy = format!("http://{}:{}", parsing_detail.ip_address, parsing_detail.port);
+            env::set_var("http_proxy", http_proxy.clone());
+            env::set_var("https_proxy", http_proxy.clone());
+            //trace!("代理设置成功{0}", http_proxy.to_string());
+            true
+        } else {
+            //trace!("无法开启代理:环境变量获取失败:{:?}", parsing_detail);
+            false
+        }
+    }
+}
+
+
+
+

+ 50 - 0
exchanges/src/response_base.rs

@@ -0,0 +1,50 @@
+use serde_json::Value;
+use tokio::time::Instant;
+
+/**交易所返回数据处理之后,同意保存格式,为了内部其他接口调用*/
+#[derive(Debug, Clone)]
+pub struct ResponseData {
+    pub tag: String,
+    pub code: i16,
+    pub message: String,
+    pub channel: String,
+    pub data: Value,
+    pub ins: Instant,           // 数据接收的ins
+    pub time: i64,              // 数据接受的时间
+    pub reach_time: i64,        // 远程数据时间 弃用
+    pub data_type: String       // 數據類型, 例如 bybit 深度信息:snapshot(全量),delta(增量)
+}
+
+impl ResponseData {
+    pub fn new(tag: String, code: i16, message: String, data: Value) -> ResponseData {
+        ResponseData {
+            tag,
+            code,
+            message,
+            data,
+            channel: "".to_string(),
+            time: 0,
+            reach_time: 0,
+            data_type: String::new(),
+            ins: Instant::now(),
+        }
+    }
+    pub fn error(tag: String, message: String) -> ResponseData {
+        ResponseData {
+            tag,
+            code: -1,
+            message: format!("{}", &message),
+            data: Value::Null,
+            channel: "".to_string(),
+            time: 0,
+            reach_time: 0,
+            data_type: String::new(),
+            ins: Instant::now(),
+        }
+    }
+
+    pub fn to_string(&self) -> String {
+        format!("{:?}", self)
+    }
+}
+

+ 439 - 0
exchanges/src/socket_tool.rs

@@ -0,0 +1,439 @@
+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::time::{Duration};
+
+use chrono::Utc;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use futures_util::{future, pin_mut, SinkExt, StreamExt};
+use futures_util::stream::{SplitSink, SplitStream};
+use ring::hmac;
+use serde_json::{json, Value};
+use tokio::net::TcpStream;
+use tokio::sync::Mutex;
+use tokio::time::Instant;
+use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::proxy;
+use crate::proxy::{ProxyEnum, ProxyResponseEnum};
+use crate::response_base::ResponseData;
+
+#[derive(Debug)]
+pub enum HeartbeatType {
+    Ping,
+    Pong,
+    Custom(String),
+}
+
+pub struct AbstractWsMode {}
+
+impl AbstractWsMode {
+    pub async fn ws_connected<T, PI, PO, F, B, Future>(write_to_socket_rx_arc: Arc<Mutex<UnboundedReceiver<Message>>>,
+                                                       is_first_login: bool,
+                                                       tag: String,
+                                                       is_shutdown_arc: Arc<AtomicBool>,
+                                                       handle_function: &F,
+                                                       subscribe_array: Vec<String>,
+                                                       ws_stream: WebSocketStream<MaybeTlsStream<TcpStream>>,
+                                                       message_text: T,
+                                                       message_ping: PI,
+                                                       message_pong: PO,
+                                                       message_binary: B)
+        where T: Fn(String) -> Option<ResponseData> + Copy,
+              PI: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              PO: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              F: Fn(ResponseData) -> Future + Clone,
+              B: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              Future: future::Future<Output=()> + Send + 'static,
+    {
+        let (ws_write, mut ws_read) = ws_stream.split();
+        let ws_write_arc = Arc::new(Mutex::new(ws_write));
+
+        // 将socket 的写操作与【写通道(外部向socket写)】链接起来,将数据以ok的结构体封装进行传递
+        // 这里是形成链式操作,如果要将外界的信息传进来(使用socket查单、下单之类的,部分交易所可以支持),就要这要弄
+        let mut write_to_socket_rx = write_to_socket_rx_arc.lock().await;
+        let ws_write_channel_clone = Arc::clone(&ws_write_arc);
+        let stdin_to_ws = async {
+            while let Some(message) = write_to_socket_rx.next().await {
+                let mut write_lock2 = ws_write_channel_clone.lock().await;
+                write_lock2.send(message).await?;
+            }
+            Ok::<(), Error>(())
+        };
+
+        // 如果不需要事先登录,则直接订阅消息
+        if !is_first_login {
+            info!("订阅长度: {}", subscribe_array.len());
+            for s in &subscribe_array {
+                info!("订阅内容:{} ", s);
+
+                let mut write_lock = ws_write_arc.lock().await;
+                write_lock.send(Message::Text(s.parse().unwrap())).await.expect("订阅消息失败");
+            }
+        }
+
+        let ws_write_inner = Arc::clone(&ws_write_arc);
+        let ws_to_stdout = async {
+            while let Some(message) = ws_read.next().await {
+                if !is_shutdown_arc.load(Ordering::Relaxed) {
+                    continue;
+                }
+
+                let response_data = AbstractWsMode::analysis_message(message, message_text, message_ping, message_pong, message_binary);
+                // let response_data = func(message);
+                if response_data.is_some() {
+                    let mut data = response_data.unwrap();
+                    data.tag = tag.clone();
+
+                    let code = data.code.clone();
+
+                    // if code == 200 {
+                    //     let mut data_c = data.clone();
+                    //     data_c.ins = Instant::now();
+                    //     data_c.time = Utc::now().timestamp_millis();
+                    //
+                    //     handle_function(data_c).await;
+                    // }
+
+                    /*
+                        200 -正确返回
+                       -200 -登录成功
+                       -201 -订阅成功
+                       -300 -客户端收到服务器心跳ping,需要响应
+                       -301 -客户端收到服务器心跳pong,需要响应
+                       -302 -客户端收到服务器心跳自定义,需要响应自定义
+                    */
+                    match code {
+                        200 =>{
+                            let mut data_c = data.clone();
+                            data_c.ins = Instant::now();
+                            data_c.time = Utc::now().timestamp_millis();
+
+                            handle_function(data_c).await;
+                        }
+                        -200 => {
+                            //登录成功
+                            info!("ws登录成功:{:?}", data);
+                            info!("订阅内容:{:?}", subscribe_array.clone());
+                            for s in &subscribe_array {
+                                let mut write_lock = ws_write_arc.lock().await;
+                                write_lock.send(Message::Text(s.parse().unwrap())).await.expect("订阅消息失败");
+                            }
+                            info!("订阅完成!");
+                        }
+                        -201 => {
+                            //订阅成功
+                            trace!("订阅成功:{:?}", data);
+                        }
+                        -300 => {
+                            //服务器发送心跳 ping 给客户端,客户端需要pong回应
+                            trace!("服务器响应-ping");
+                            if data.data != Value::Null {
+                                let mut ws_write = ws_write_inner.lock().await;
+                                ws_write.send(Message::Pong(Vec::from(data.data.to_string()))).await?;
+                                trace!("客户端回应服务器-pong");
+                            }
+                        }
+                        -301 => {
+                            //服务器发送心跳 pong 给客户端,客户端需要ping回应
+                            trace!("服务器响应-pong");
+                            if data.data != Value::Null {
+                                let mut ws_write = ws_write_inner.lock().await;
+                                ws_write.send(Message::Ping(Vec::from(data.data.to_string()))).await?;
+                                trace!("客户端回应服务器-ping");
+                            }
+                        }
+                        -302 => {
+                            //客户端收到服务器心跳自定义,需要响应自定义
+                            trace!("特定字符心跳,特殊响应:{:?}", data);
+                            let mut ws_write = ws_write_inner.lock().await;
+                            ws_write.send(Message::Text(data.data.to_string())).await?;
+                            trace!("特殊字符心跳-回应完成");
+                        }
+                        _ => {
+                            trace!("未知:{:?}",data);
+                        }
+                    }
+                }
+            }
+            Ok::<(), Error>(())
+        };
+
+        //必须操作。,因为不同于其他的高级语言,有自动内存管理,所以为了防范地址改变,所以需要做此处理
+        pin_mut!(stdin_to_ws, ws_to_stdout,);
+        future::select(stdin_to_ws, ws_to_stdout).await;
+    }
+
+    //创建链接
+    pub async fn ws_connect_async<T, PI, PO, F, B, Future>(is_shutdown_arc: Arc<AtomicBool>,
+                                                           handle_function: F,
+                                                           address_url: String,
+                                                           is_first_login: bool,
+                                                           tag: String,
+                                                           subscribe_array: Vec<String>,
+                                                           write_to_socket_rx_arc: Arc<Mutex<UnboundedReceiver<Message>>>,
+                                                           message_text: T,
+                                                           message_ping: PI,
+                                                           message_pong: PO,
+                                                           message_binary: B)
+        where T: Fn(String) -> Option<ResponseData> + Copy,
+              PI: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              PO: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              B: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              F: Fn(ResponseData) -> Future + Clone,
+              Future: future::Future<Output=()> + Send + 'static,
+    {
+        //1.是否走代理
+        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
+        let proxy = match proxy::ParsingDetail::env_proxy(ProxyEnum::WS) {
+            ProxyResponseEnum::NO => {
+                // trace!("非 代理");
+                None
+            }
+            ProxyResponseEnum::YES(proxy) => {
+                // trace!("代理");
+                Option::from(proxy)
+            }
+        };
+
+        match connect_async(address_url.clone(), proxy).await {
+            Ok((ws_stream, _)) => {
+                info!("socket 链接成功,{}。", address_url);
+
+                Self::ws_connected(write_to_socket_rx_arc,
+                                   is_first_login,
+                                   tag,
+                                   is_shutdown_arc,
+                                   &handle_function,
+                                   subscribe_array.clone(),
+                                   ws_stream,
+                                   message_text,
+                                   message_ping,
+                                   message_pong,
+                                   message_binary).await;
+            }
+            Err(e) => {
+                error!("WebSocket 握手失败:{:?}", e);
+            }
+        }
+    }
+
+    //心跳包
+    pub async fn ping_or_pong(write_tx_clone: Arc<Mutex<UnboundedSender<Message>>>, h_type: HeartbeatType, millis: u64) {
+        loop {
+            tokio::time::sleep(Duration::from_millis(millis)).await;
+            let write_tx_clone = write_tx_clone.lock().await;
+            write_tx_clone.unbounded_send(
+                match h_type {
+                    HeartbeatType::Ping => {
+                        Message::Ping(Vec::from("Ping"))
+                    }
+                    HeartbeatType::Pong => {
+                        Message::Pong(Vec::from("Pong"))
+                    }
+                    HeartbeatType::Custom(ref str) => {
+                        Message::Text(str.parse().unwrap())
+                    }
+                }
+            ).expect("发送失败");
+            trace!("发送指令-心跳:{:?}",h_type);
+        }
+    }
+    //数据解析
+    pub fn analysis_message<T, PI, PO, B>(message: Result<Message, Error>,
+                                          message_text: T,
+                                          message_ping: PI,
+                                          message_pong: PO,
+                                          message_binary: B) -> Option<ResponseData>
+        where T: Fn(String) -> Option<ResponseData>,
+              PI: Fn(Vec<u8>) -> Option<ResponseData>,
+              PO: Fn(Vec<u8>) -> Option<ResponseData>,
+              B: Fn(Vec<u8>) -> Option<ResponseData>
+    {
+        match message {
+            Ok(Message::Text(text)) => message_text(text),
+            Ok(Message::Ping(pi)) => message_ping(pi),
+            Ok(Message::Pong(po)) => message_pong(po),
+            Ok(Message::Binary(s)) => message_binary(s), //二进制WebSocket消息
+            Ok(Message::Close(c)) => {
+                let message_str = format!("关闭指令:{:?}", c);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(), 0, message_str, Value::Null))
+            }
+            Ok(Message::Frame(f)) => {
+                //原始帧 正常读取数据不会读取到该 信息类型
+                let message_str = format!("意外读取到原始帧:{:?}", f);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(), -2, message_str, Value::Null))
+            }
+            Err(e) => {
+                let message_str = format!("服务器响应:{:?}", e);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(), -1, message_str, Value::Null))
+            }
+        }
+    }
+    //发送数据
+    pub async fn send_subscribe(write_tx_clone: Arc<Mutex<UnboundedSender<Message>>>, message: Message) -> bool {
+        let write_tx_clone = write_tx_clone.lock().await;
+        write_tx_clone.unbounded_send(message.clone()).unwrap();
+        trace!("发送指令:{:?}",message);
+        true
+    }
+}
+
+//创建链接
+pub async fn ws_connect_async(address_url: String) -> (SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>,
+                                                       SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>) {
+    //1.是否走代理
+    /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
+    let proxy = match proxy::ParsingDetail::env_proxy(ProxyEnum::WS) {
+        ProxyResponseEnum::NO => {
+            trace!("非 代理");
+            None
+        }
+        ProxyResponseEnum::YES(proxy) => {
+            trace!("代理");
+            Option::from(proxy)
+        }
+    };
+
+    let (ws_stream, _) = connect_async(address_url, proxy).await.expect("链接失败!");
+    trace!("WebSocket 握手完成。");
+    ws_stream.split()
+}
+
+
+pub async fn client(add_url: String) {
+    let proxy = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(
+        127,
+        0,
+        0,
+        1)
+    ), 7890);
+
+
+    //创建通道 开启线程,向通道写入数据
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, read_rx) = futures_channel::mpsc::unbounded();
+    tokio::spawn(write_sell(write_tx));
+
+
+    //创建socket,,并且读写分离
+    let (ws_stream, _) = connect_async(add_url, Option::from(proxy)).await.expect("Failed to connect");
+    trace!("WebSocket handshake has been successfully completed");
+    let (write, read) = ws_stream.split();
+
+    //将socket 的写操作与 写通道链接起来,将数据以ok的结构体封装进行传递
+    let stdin_to_ws = write_rx.map(Ok).forward(write);
+    let ws_to_stdout = {
+        trace!("---1");
+        //读,循环读取,然后拿到 message,,然后开启异步处理 message,
+        let result = read.for_each(|message| async {
+            read_tx.unbounded_send(message.unwrap()).unwrap();
+        });
+        trace!("---3");
+        result
+    };
+
+    tokio::spawn(read_sell(read_rx));
+
+    //必须操作。,因为不同于其他的高级语言,有自动内存管理,所以为了防范地址改变,所以需要做此处理
+    pin_mut!(stdin_to_ws, ws_to_stdout);
+    future::select(stdin_to_ws, ws_to_stdout).await;
+}
+
+
+//模拟 业务场景中 发送指令给目标交易所
+async fn write_sell(tx: UnboundedSender<Message>) {
+    let _str = json!({
+                "op": "subscribe",
+                "args": [
+                        {
+                        // "channel":"orders",
+                        // "instType":"SWAP",
+                        // "instFamily":"BTC-USDT"
+                        "channel":"books5",
+                        "instId":"BTC-USDT"
+                        }
+                    ]
+            });
+    let str_array: Vec<String> = vec![
+        // log_in_to_str(),
+        // str.to_string(),
+    ];
+
+    let i = 0;
+    loop {
+        if str_array.len() > i {
+            let send_str = str_array.get(i).unwrap();
+            tx.unbounded_send(Message::Text(send_str.to_string())).unwrap();
+        }
+        tokio::time::sleep(Duration::from_secs(5)).await;
+        tx.unbounded_send(Message::Ping(Vec::from("Ping"))).unwrap();
+        tx.unbounded_send(Message::Ping(Vec::from("Pong"))).unwrap();
+    }
+}
+
+
+async fn read_sell(mut rx: futures_channel::mpsc::UnboundedReceiver<Message>) {
+    loop {
+        if let Some(message) = rx.next().await {
+            match message {
+                Message::Text(s) => {
+                    trace!("Text: {}", s);
+                }
+                Message::Binary(s) => {
+                    trace!("Binary: {:?}", s);
+                }
+                Message::Ping(s) => {
+                    trace!("Ping: {:?}", s);
+                }
+                Message::Pong(s) => {
+                    trace!("Pong: {:?}", s);
+                }
+                Message::Close(s) => {
+                    trace!("Close: {:?}", s);
+                }
+                Message::Frame(s) => {
+                    trace!("Frame: {:?}", s);
+                }
+            }
+        }
+        tokio::time::sleep(Duration::from_millis(1)).await
+    }
+}
+
+
+pub fn log_in_to_str() -> String {
+    let mut login_json_str = "".to_string();
+
+    let access_key: String = "".to_string();
+    let secret_key: String = "".to_string();
+    let passphrase: String = "".to_string();
+
+    if access_key.len() > 0 || secret_key.len() > 0 || passphrase.len() > 0 {
+        let timestamp = Utc::now().timestamp().to_string();
+        // 时间戳 + 请求类型+ 请求参数字符串
+        let message = format!("{}GET{}", timestamp, "/users/self/verify");
+        let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+        let result = ring::hmac::sign(&hmac_key, &message.as_bytes());
+        let sign = base64::encode(result);
+
+        let login_json = json!({
+                              "op": "login",
+                                "args": [{
+                                "apiKey": access_key,
+                                "passphrase": passphrase,
+                                "timestamp": timestamp,
+                                "sign": sign  }]
+                        });
+
+        // trace!("---login_json:{0}", login_json.to_string());
+        // trace!("--登录:{}", login_json.to_string());
+        login_json_str = login_json.to_string();
+    }
+    login_json_str
+}

+ 9 - 0
exchanges/src/utils.rs

@@ -0,0 +1,9 @@
+// use chrono::Utc;
+
+// pub fn get_time_microsecond() -> i64 {
+//     let now = Utc::now();
+//     let total_micros = now.timestamp_micros(); //微妙
+//     total_micros
+// }
+//
+

+ 94 - 0
exchanges/tests/binance_spot_test.rs

@@ -0,0 +1,94 @@
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+use futures_util::StreamExt;
+
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::Message;
+use tracing::trace;
+
+use exchanges::binance_spot_ws::{BinanceSpotLogin, BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
+use exchanges::socket_tool::AbstractWsMode;
+
+// 账号密码
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let login_param = BinanceSpotLogin {
+        api_key: ACCESS_KEY.to_string(),
+        api_secret: SECRET_KEY.to_string(),
+    };
+    let mut ws = get_ws(None);
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        // BinanceSpotSubscribeType::PuBookTicker,
+        // BinanceSpotSubscribeType::PuAggTrade,
+        BinanceSpotSubscribeType::PuDepth20levels100ms,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+fn get_ws(login_param: Option<BinanceSpotLogin>) -> BinanceSpotWs {
+    let binance_ws = BinanceSpotWs::new(false,
+                                        login_param, BinanceSpotWsType::PublicAndPrivate,
+    );
+    binance_ws
+}
+

+ 246 - 0
exchanges/tests/binance_swap_test.rs

@@ -0,0 +1,246 @@
+use std::cmp::max;
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use tokio::sync::Mutex;
+use tracing::{info, trace};
+
+use exchanges::binance_swap_rest::BinanceSwapRest;
+use exchanges::binance_swap_ws::{BinanceSwapLogin, BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let mut ws = get_ws(None);
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        BinanceSwapSubscribeType::PuBookTicker,
+        // BinanceSwapSubscribeType::PuAggTrade,
+        // BinanceSwapSubscribeType::PuDepth20levels100ms,
+    ]);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        let mut max_delay = 0i64;
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(data)) = read_rx.try_next() {
+                // 消息被忽略
+                let mut trace_stack = TraceStack::new(0, Instant::now());
+                trace_stack.on_before_unlock_core();
+                trace_stack.on_after_network(data.time);
+
+                let delay = trace_stack.before_unlock_core - trace_stack.after_network;
+                max_delay = max(max_delay, delay);
+                info!("{}us, max={}us", delay, max_delay);
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //11 点31 分
+
+    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
+    // //创建读写通道
+    // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+    // // 封装 write_tx 到 Arc 和 Mutex
+    // let write_tx_am = Arc::new(Mutex::new(write_tx));
+    //
+    // //对象
+    // let mut ws = get_ws(None);
+    // // 币对
+    // ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    // //订阅
+    // ws.set_subscribe(vec![
+    //     BinanceSwapSubscribeType::PuBookTicker,
+    //     BinanceSwapSubscribeType::PuAggTrade,
+    //     BinanceSwapSubscribeType::PuDepth20levels100ms,
+    // ]);
+    //
+    //
+    // //模拟业务场景 开启链接
+    // let is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone1 = Arc::clone(&write_tx_am);
+    // let t1 = tokio::spawn(async move {
+    //     ws.ws_connect_async(is_shutdown_arc_clone, write_tx_clone1, write_rx, &read_tx).await.unwrap();
+    //     trace!("ws_connect_async 完成");
+    // });
+    //
+    // //模拟业务场景 一直监听数据
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Some(data) = read_rx.next().await {
+    //             trace!("读取数据data:{:?}",data)
+    //         }
+    //     }
+    //     trace!("数据读取退出 完成");
+    // });
+    //
+    //
+    // //模拟用户主动写入数据
+    // // let write_tx_clone2 = Arc::clone(&write_tx_am);
+    // // let t3 = tokio::spawn(async move {
+    // //     //模拟心跳
+    // //     loop {
+    // //         tokio::time::sleep(Duration::from_millis(5000)).await;
+    // //         let mut write_tx_clone = write_tx_clone2.lock().unwrap();
+    // //         match write_tx_clone.unbounded_send(Message::Pong(Vec::from("pong"))) {
+    // //             Ok(_) => {
+    // //                 trace!("发送心跳");
+    // //                 continue;
+    // //             }
+    // //             Err(_) => {
+    // //                 break;
+    // //             }
+    // //         }
+    // //     }
+    // //     trace!("主动推出 完成");
+    // // });
+    // // tokio::try_join!(y,y1,y2).unwrap();
+    // tokio::try_join!(t1,t2).unwrap();
+    // trace!("323123213");
+}
+
+//rest-获取服务器时间
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_server_time().await;
+    trace!(?rep_data)
+}
+
+//rest-获取交易规则和交易对
+#[tokio::test]
+async fn rest_get_exchange_info_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_exchange_info().await;
+    trace!(?rep_data)
+}
+
+//rest-账户信息
+#[tokio::test]
+async fn rest_get_account_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_account().await;
+    trace!(?rep_data)
+}
+
+
+//rest-根据币对 撤销全部订单
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn rest_cancel_order_all_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+
+    let rep_data1 = rest.get_server_time().await;
+    trace!(?rep_data1);
+
+    trace!("开始时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
+    let rep_data = rest.cancel_order_all("BTCUSDT".to_string()).await;
+    trace!(?rep_data);
+    trace!("结束时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
+}
+
+//rest-账户成交历史
+#[tokio::test]
+async fn rest_get_user_trades_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_user_trades("BTCUSDT".to_string(), -1, -1, 500).await;
+    trace!(?rep_data)
+}
+
+
+fn get_ws(btree_map: Option<BinanceSwapLogin>) -> BinanceSwapWs {
+    let binance_ws = BinanceSwapWs::new(false,
+                                        btree_map,
+                                        BinanceSwapWsType::PublicAndPrivate);
+    binance_ws
+}
+
+fn get_rest() -> BinanceSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+
+    let ba_exc = BinanceSwapRest::new(false, btree_map);
+    ba_exc
+}

+ 129 - 0
exchanges/tests/bingx_swap_test.rs

@@ -0,0 +1,129 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use serde_json::json;
+use tokio::sync::Mutex;
+use tracing::trace;
+use exchanges::bingx_swap_rest::BingxSwapRest;
+
+use exchanges::bingx_swap_ws::{BingxSwapLogin, BingxSwapSubscribeType, BingxSwapWs, BingxSwapWsType};
+use exchanges::response_base::ResponseData;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(_)) = read_rx.try_next() {
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let fun = move |data: ResponseData| {
+        async move {
+            // trace!("---传入的方法~~~~{:?}", data);
+        }
+    };
+    let param = BingxSwapLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
+    let t1 = tokio::spawn(async move {
+        let mut ws = get_ws(Option::from(param), BingxSwapWsType::PublicAndPrivate);
+        ws.set_symbols(vec!["BTC_USDT".to_string(), "ETC_USDT".to_string()]);
+        ws.set_subscribe(vec![
+            // BingxSwapSubscribeType::PuFuturesTrades,
+            // BingxSwapSubscribeType::PuFuturesDepth,
+            BingxSwapSubscribeType::PuFuturesRecords,
+
+        ]);
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+fn get_ws(btree_map: Option<BingxSwapLogin>, ws_type: BingxSwapWsType) -> BingxSwapWs {
+    let bingx_ws = BingxSwapWs::new(false,btree_map, ws_type);
+    bingx_ws
+}
+
+
+//查詢合約基礎信息
+#[tokio::test]
+async fn rest_get_market_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_market(json!({
+
+    })).await;
+    println!("bingx--查詢合約基礎信息--{:?}", req_data);
+}
+
+
+fn get_rest() -> BingxSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let bingx_exc = BingxSwapRest::new(false, btree_map.clone());
+    bingx_exc
+}
+

+ 515 - 0
exchanges/tests/bitget_spot_test.rs

@@ -0,0 +1,515 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tracing::trace;
+
+use exchanges::bitget_spot_rest::BitgetSpotRest;
+use exchanges::bitget_spot_ws::{BitgetSpotLogin, BitgetSpotSubscribeType, BitgetSpotWs, BitgetSpotWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe_pu() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let  is_shutdown_arc = Arc::new(AtomicBool::new(true));
+    let mut ws = get_ws(None, BitgetSpotWsType::Public).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        BitgetSpotSubscribeType::PuTicker,
+        BitgetSpotSubscribeType::PuCandle1m,
+        BitgetSpotSubscribeType::PuTrade,
+        BitgetSpotSubscribeType::PuBooks5,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe_pr() {
+    global::log_utils::init_log_with_trace();
+
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let login_param = BitgetSpotLogin {
+        api_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        passphrase_key: PASS_KEY.to_string(),
+    };
+    let mut ws = get_ws(None, BitgetSpotWsType::Private).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        BitgetSpotSubscribeType::PuTicker,
+        BitgetSpotSubscribeType::PuCandle1m,
+        BitgetSpotSubscribeType::PuTrade,
+        BitgetSpotSubscribeType::PuBooks5,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+//读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+//rest-获取系统时间
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_server_time().await;
+    trace!(?rep_data)
+}
+
+
+//rest-获取账户信息
+#[tokio::test]
+async fn rest_get_account_info_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_info().await;
+    trace!(?rep_data)
+}
+
+//rest-获取账户币种资产
+#[tokio::test]
+async fn rest_get_account_assets_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_assets().await;
+    trace!(?rep_data)
+}
+
+//rest-获取币种信息
+#[tokio::test]
+async fn rest_get_coins_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_coins("USDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取交易对信息
+#[tokio::test]
+async fn rest_get_symbols_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_symbols("BTCUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取现货VIP费率
+#[tokio::test]
+async fn rest_get_vip_fee_rate_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_vip_fee_rate().await;
+    trace!(?rep_data)
+}
+
+//rest-获取行情信息
+#[tokio::test]
+async fn rest_get_tickers_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_tickers("BTCUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取合并交易深度
+#[tokio::test]
+async fn rest_get_merge_depth_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_merge_depth("BTCUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取K线数据
+#[tokio::test]
+async fn rest_get_candles_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_candles("BTCUSDT".to_string(), "1min".to_string(), "1697701550192".to_string(), "1697701556192".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史K线数据
+#[tokio::test]
+async fn rest_get_history_candles_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_history_candles("BTCUSDT".to_string(), "1min".to_string(), "1697701556192".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取最近成交数据
+#[tokio::test]
+async fn rest_get_market_fills_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_market_fills("BTCUSDT".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史成交数据
+#[tokio::test]
+async fn rest_get_fills_history_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_market_fills_history("BTCUSDT".to_string(), "1697701550192".to_string(), "1697701556192".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-下单
+#[tokio::test]
+async fn rest_spot_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    //市价单
+    let mut rest = get_rest();
+    let params = serde_json::json!({
+            // "symbol":"CELRUSDT",
+            // "side":"sell",
+            // "orderType":"market",
+            // "force":"fok",
+            // "size":"887",
+            // "clientOid":"7d8zd4d_3",
+         });
+
+    //限价单
+    let params = serde_json::json!({
+            // "symbol":"CELRUSDT",
+            // "side":"buy",
+            // "orderType":"limit",
+            // "force":"gtc",
+            // "price":"0.01001",
+            // "size":"10",
+            // "clientOid":"7d8zd4d_z1",
+         });
+
+    let rep_data = rest.spot_order(params).await;
+    trace!(?rep_data)
+}
+
+
+//rest-撤单
+#[tokio::test]
+async fn rest_spot_cancel_order_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.spot_cancel_order("CELRUSDT".to_string(), "".to_string(), "1".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-批量撤单
+#[tokio::test]
+async fn rest_spot_cancel_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let v = serde_json::json!({
+            "orderId":"1073370944162058240",
+            "clientOid":"1073370944162058240"
+        });
+    let rep_data = rest.spot_cancel_orders("CELRUSDT".to_string(), vec![v]).await;
+    trace!(?rep_data)
+}
+
+//rest-按币对撤单
+#[tokio::test]
+async fn rest_spot_cancel_symbol_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.spot_cancel_symbol_orders("CELRUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取订单详情
+#[tokio::test]
+async fn rest_get_order_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_order("".to_string(), "1".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取当前委托列表
+#[tokio::test]
+async fn rest_get_unfilled_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_unfilled_orders("CELRUSDT".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史委托列表
+#[tokio::test]
+async fn rest_get_history_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_history_orders("CELRUSDT".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取成交明细
+#[tokio::test]
+async fn rest_get_fills_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_fills("CELRUSDT".to_string(),
+                                  "1".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取成交明细
+#[tokio::test]
+async fn rest_spot_place_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    //限价-委托单
+    let params = serde_json::json!({
+         });
+
+    let rep_data = rest.spot_place_plan_order(params).await;
+    trace!(?rep_data)
+}
+
+//rest-修改计划委托
+#[tokio::test]
+async fn rest_update_place_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    //限价-委托单
+    let params = serde_json::json!({
+         });
+
+    let rep_data = rest.update_place_plan_order(params).await;
+    trace!(?rep_data)
+}
+
+//rest-撤销计划委托
+#[tokio::test]
+async fn rest_cancel_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_plan_order("32131".to_string(), "3211".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取当前计划委托
+#[tokio::test]
+async fn rest_get_current_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_current_plan_order("CELRUSDT".to_string(),
+                                               "1".to_string(),
+                                               "".to_string(),
+                                               "".to_string(),
+                                               "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史计划委托
+#[tokio::test]
+async fn rest_get_history_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_history_plan_order("CELRUSDT".to_string(),
+                                               "1697701550192".to_string(),
+                                               "1697701580192".to_string(),
+                                               "100".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-批量撤销计划委托
+#[tokio::test]
+async fn rest_cancel_plan_orders_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_plan_orders(vec![]).await;
+    trace!(?rep_data)
+}
+
+//rest-划转
+#[tokio::test]
+async fn rest_wallet_transfer_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.wallet_transfer("".to_string(),
+                                        "".to_string(),
+                                        "".to_string(),
+                                        "".to_string(),
+                                        "".to_string(),
+                                        "".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取账单流水
+#[tokio::test]
+async fn rest_get_account_bills_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_bills("".to_string(),
+                                          "".to_string(),
+                                          "".to_string(),
+                                          "".to_string(), ).await;
+    trace!(?rep_data)
+}
+
+
+async fn get_ws(btree_map: Option<BitgetSpotLogin>, type_v: BitgetSpotWsType) -> BitgetSpotWs {
+    let mut ku_ws = BitgetSpotWs::new(false, btree_map.clone(), type_v);
+    ku_ws
+}
+
+fn get_rest() -> BitgetSpotRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let mut ku_exc = BitgetSpotRest::new(false, btree_map);
+    ku_exc
+}

+ 145 - 0
exchanges/tests/bitget_swap_test.rs

@@ -0,0 +1,145 @@
+use std::collections::BTreeMap;
+use serde_json::json;
+use tracing::{info};
+use exchanges::bitget_swap_rest::BitgetSwapRest;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+// 测试账户信息获取
+#[tokio::test]
+async fn rest_get_account_info_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_info().await;
+    info!(?rep_data)
+}
+
+// 下单测试
+#[tokio::test]
+async fn post_order_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let params = json!({
+        "symbol": "BTCUSDT",
+        "productType": "USDT-FUTURES",
+        "marginMode": "crossed",
+        "marginCoin": "USDT",
+        "size": "0.001",
+        "side": "buy",
+        "tradeSide": "close",
+        "orderType": "market",
+        "clientOid": "1130615113245"
+    });
+    let response = rest.swap_order(params).await;
+
+    info!(?response)
+}
+
+// 撤单测试
+#[tokio::test]
+async fn cancel_order_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let params = json!({
+        "symbol": "ethusdt",
+        "productType": "USDT-FUTURES",
+        "clientOid": "1130615111",
+    });
+    let response = rest.cancel_order(params).await;
+
+    info!(?response)
+}
+
+// 获取订单详情测试
+#[tokio::test]
+async fn get_order_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let params = json!({
+        "symbol": "ETHUSDT",
+        "productType": "USDT-FUTURES",
+        "clientOid": "1130615132",
+    });
+    let response = rest.get_order(params).await;
+
+    info!(?response)
+}
+
+// 获取当前的pending订单
+#[tokio::test]
+async fn get_pending_orders_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let response = rest.get_pending_orders().await;
+
+    info!(?response)
+}
+
+// 设置杠杆测试
+#[tokio::test]
+async fn set_leverage_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+
+
+    let params = json!({
+        "symbol": "ETHUSDT",
+        "productType": "USDT-FUTURES",
+        "marginCoin": "USDT",
+        "leverage": "5"
+    });
+    let response = rest.set_leverage(params).await;
+
+    info!(?response)
+}
+
+// 设置持仓模式
+#[tokio::test]
+async fn set_position_mode_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+
+
+    let params = json!({
+        "productType": "USDT-FUTURES",
+        "posMode": "hedge_mode",
+    });
+    let response = rest.set_position_mode(params).await;
+
+    info!(?response)
+}
+
+// 获取仓位信息
+#[tokio::test]
+async fn get_single_position_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let params = json!({
+        "productType": "USDT-FUTURES",
+        "symbol": "ETHUSDT",
+        "marginCoin": "USDT"
+    });
+    let response = rest.get_single_position(params).await;
+
+    info!(?response)
+}
+
+fn get_rest() -> BitgetSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    BitgetSwapRest::new(false, btree_map)
+}

+ 320 - 0
exchanges/tests/bybit_swap_test.rs

@@ -0,0 +1,320 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tracing::trace;
+
+use exchanges::bybit_swap_rest::BybitSwapRest;
+use exchanges::bybit_swap_ws::{BybitSwapLogin, BybitSwapSubscribeType, BybitSwapWs, BybitSwapWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe_pu() {
+    global::log_utils::init_log_with_trace();
+
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+
+    let mut ws = get_ws(None, BybitSwapWsType::Public).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        // BybitSwapSubscribeType::PuOrderBook1,
+        // BybitSwapSubscribeType::PuOrderBook50,
+        // BybitSwapSubscribeType::PuBlicTrade,
+        BybitSwapSubscribeType::PuTickers,
+    ]);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe_pr() {
+    global::log_utils::init_log_with_trace();
+
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let logparam = BybitSwapLogin {
+        api_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+    };
+
+    let mut ws = get_ws(Option::from(logparam), BybitSwapWsType::Private).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        BybitSwapSubscribeType::PrPosition,
+        // BybitSwapSubscribeType::PrExecution,
+        // BybitSwapSubscribeType::PrOrder,
+        // BybitSwapSubscribeType::PrWallet,
+    ]);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+//rest-服務器時間
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_server_time().await;
+    println!("Bybit--服務器時間--{:?}", req_data);
+}
+
+//rest-查詢最新行情信息
+#[tokio::test]
+async fn rest_get_tickers_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_tickers("DOGEUSDT".to_string()).await;
+    println!("Bybit--查詢最新行情信息--{:?}", req_data);
+}
+
+//rest-查詢市場價格K線數據
+#[tokio::test]
+async fn rest_get_kline_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_kline("DOGEUSDT".to_string()).await;
+    println!("Bybit--查詢市場價格K線數據--{:?}", req_data);
+}
+
+
+//rest-查詢公告
+#[tokio::test]
+async fn rest_get_announcements_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_announcements().await;
+    println!("Bybit--查詢公告--{:?}", req_data);
+}
+
+//rest-查詢可交易產品的規格信息
+#[tokio::test]
+async fn rest_get_instruments_info_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_instruments_info("BTCUSDT".to_string()).await;
+    println!("Bybit--查詢可交易產品的規格信息--{:?}", req_data);
+}
+
+
+//rest-查詢錢包餘額
+#[tokio::test]
+async fn rest_get_account_balance_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_account_balance("USDT".to_string()).await;
+    println!("Bybit--查詢錢包餘額--{:?}", req_data);
+}
+
+//rest-查看持仓信息
+#[tokio::test]
+async fn rest_get_positions_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_positions("DOGEUSDT".to_string(), "".to_string()).await;
+    println!("Bybit--查看持仓信息--{:?}", req_data);
+}
+
+//rest-设置持仓模式
+#[tokio::test]
+async fn rest_set_position_mode_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.set_position_mode("DOGEUSDT".to_string(), 3).await;
+    println!("Bybit--设置持仓模式--{:?}", req_data);
+}
+
+//rest-設置槓桿
+#[tokio::test]
+async fn rest_set_leverage_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.set_leverage(
+        "DOGEUSDT".to_string(), "1".to_string()).await;
+    println!("Bybit--設置槓桿--{:?}", req_data);
+}
+
+
+//rest-創建委託單
+#[tokio::test]
+async fn rest_swap_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let params = serde_json::json!({
+            "category":"linear",
+            "symbol":"DOGEUSDT",
+            "orderType":"Limit",
+            "side":"Buy",
+            "qty":"1",
+            "price":"0.085",
+         });
+    let req_data = ret.swap_order(params).await;
+    println!("Bybit--創建委託單--{:?}", req_data);
+}
+
+
+//rest-查詢實時委託單
+#[tokio::test]
+async fn rest_get_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_order("LINKUSDT".to_string(),
+                                 "".to_string(), "".to_string()).await;
+    println!("Bybit--查詢實時委託單--{:?}", req_data);
+}
+
+
+//rest-撤单
+#[tokio::test]
+async fn rest_cancel_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.cancel_order("DOGEUSDT".to_string(),
+                                    "1d3ea16f-cf1c-4dab-9a79-d441a2dea549".to_string(), "".to_string()).await;
+    println!("Bybit--撤单--{:?}", req_data);
+}
+
+//rest-撤銷所有訂單
+#[tokio::test]
+async fn rest_cancel_orders_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.cancel_orders("DOGEUSDT".to_string()).await;
+    println!("Bybit--撤銷所有訂單--{:?}", req_data);
+}
+
+
+async fn get_ws(btree_map: Option<BybitSwapLogin>, type_v: BybitSwapWsType) -> BybitSwapWs {
+    let ku_ws = BybitSwapWs::new(false, btree_map, type_v);
+    ku_ws
+}
+
+fn get_rest() -> BybitSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+
+    let bybit_exc = BybitSwapRest::new(false, btree_map.clone());
+    bybit_exc
+}

+ 139 - 0
exchanges/tests/coinsph_swap_test.rs

@@ -0,0 +1,139 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use serde_json::json;
+
+use tokio::sync::Mutex;
+use tracing::trace;
+use exchanges::coinsph_swap_rest::CoinsphSwapRest;
+
+use exchanges::coinsph_swap_ws::{CoinsphSwapLogin, CoinsphSwapSubscribeType, CoinsphSwapWs, CoinsphSwapWsType};
+use exchanges::response_base::ResponseData;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(_)) = read_rx.try_next() {
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let fun = move |data: ResponseData| {
+        async move {
+            // trace!("---传入的方法~~~~{:?}", data);
+        }
+    };
+    let param = CoinsphSwapLogin {
+        api_key: "".to_string(),
+        secret: "".to_string(),
+        api_memo: "".to_string(),
+    };
+    let t1 = tokio::spawn(async move {
+        let mut ws = get_ws(Option::from(param), CoinsphSwapWsType::Public);
+        ws.set_symbols(vec!["ZRX_USDT".to_string(),"BTC_USDT".to_string(), "ETC_USDT".to_string()]);
+        ws.set_subscribe(vec![
+            CoinsphSwapSubscribeType::PuFuturesTrades,
+            CoinsphSwapSubscribeType::PuFuturesDepth,
+            CoinsphSwapSubscribeType::PuFuturesRecords,
+        ]);
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+fn get_ws(btree_map: Option<CoinsphSwapLogin>, ws_type: CoinsphSwapWsType) -> CoinsphSwapWs {
+    let coinsph_ws = CoinsphSwapWs::new(false,btree_map, ws_type);
+    coinsph_ws
+}
+
+
+
+//获取服务器时间
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_server_time().await;
+    println!("Coinsph--获取服务器时间--{:?}", req_data);
+}
+
+//获取合约信息
+#[tokio::test]
+async fn rest_get_market_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_market(json!({
+            "symbol":"ETHBTC"
+    })).await;
+    println!("Coinsph--获取合约信息--{:?}", req_data);
+}
+
+
+fn get_rest() -> CoinsphSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let Coinsph_exc = CoinsphSwapRest::new( false,btree_map.clone());
+    Coinsph_exc
+}

+ 132 - 0
exchanges/tests/cointr_swap_test.rs

@@ -0,0 +1,132 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use serde_json::json;
+use tokio::sync::Mutex;
+use tracing::trace;
+use exchanges::cointr_swap_rest::CointrSwapRest;
+use exchanges::response_base::ResponseData;
+use exchanges::cointr_swap_ws::{CointrSwapLogin, CointrSwapSubscribeType, CointrSwapWs, CointrSwapWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(_)) = read_rx.try_next() {
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let fun = move |data: ResponseData| {
+        async move {
+            trace!("---传入的方法~~~~{:?}", data);
+        }
+    };
+    let param = CointrSwapLogin {
+        api_key: "".to_string(),
+        secret: "".to_string(),
+        api_memo: "".to_string(),
+    };
+    let t1 = tokio::spawn(async move {
+        let mut ws = get_ws(Option::from(param), CointrSwapWsType::PublicAndPrivate);
+        ws.set_symbols(vec![
+            "BTC_USDT".to_string()
+            // , "ARB_USDT".to_string(),
+            //                  "ETH_USDT".to_string(),
+        ]);
+        ws.set_subscribe(vec![
+            CointrSwapSubscribeType::PuFuturesTrades,
+            CointrSwapSubscribeType::PuFuturesDepth,
+            CointrSwapSubscribeType::PuFuturesRecords,
+        ]);
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+fn get_ws(btree_map: Option<CointrSwapLogin>, ws_type: CointrSwapWsType) -> CointrSwapWs {
+    let cointr_ws = CointrSwapWs::new(false, btree_map, ws_type);
+    cointr_ws
+}
+
+
+//查詢合約基礎信息
+#[tokio::test]
+async fn rest_get_market_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_market(json!({
+
+    })).await;
+    println!("Cointr--查詢合約基礎信息--{:?}", req_data);
+}
+
+
+fn get_rest() -> CointrSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let cointr_exc = CointrSwapRest::new(false, btree_map.clone());
+    cointr_exc
+}
+

+ 86 - 0
exchanges/tests/crypto_spot_test.rs

@@ -0,0 +1,86 @@
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tracing::trace;
+
+use exchanges::crypto_spot_ws::{CryptoSpotLogin, CryptoSpotSubscribeType, CryptoSpotWs, CryptoSpotWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_pu() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let mut ws = get_ws(None, CryptoSpotWsType::Public);
+    ws.set_symbols(vec!["BTC_USD".to_string()]);
+    ws.set_subscribe(vec![
+        // CryptoSpotSubscribeType::PuBook,
+        // CryptoSpotSubscribeType::PuTicker,
+        // CryptoSpotSubscribeType::PuTrade,
+        CryptoSpotSubscribeType::PuCandlestick,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+fn get_ws(btree_map: Option<CryptoSpotLogin>, ws_type: CryptoSpotWsType) -> CryptoSpotWs {
+    let binance_ws = CryptoSpotWs::new(false,
+                                       btree_map,
+                                       ws_type);
+    binance_ws
+}

+ 181 - 0
exchanges/tests/gate_swap_test.rs

@@ -0,0 +1,181 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tracing::trace;
+
+use exchanges::gate_swap_rest::GateSwapRest;
+use exchanges::gate_swap_ws::{GateSwapLogin, GateSwapSubscribeType, GateSwapWs, GateSwapWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let param = GateSwapLogin {
+        api_key: ACCESS_KEY.to_string(),
+        secret: SECRET_KEY.to_string(),
+    };
+
+    let mut ws = get_ws(Option::from(param));
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        // GateSwapSubscribeType::PuFuturesOrderBook,
+        GateSwapSubscribeType::PuFuturesCandlesticks,
+        GateSwapSubscribeType::PuFuturesTrades,
+
+        // GateSwapSubscribeType::PrFuturesBalances("".to_string()),
+        // GateSwapSubscribeType::PrFuturesOrders("".to_string()),
+        // GateSwapSubscribeType::PrFuturesPositions("".to_string()),
+    ]);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+//rest-设置持仓模式
+#[tokio::test]
+async fn rest_cancel_order_all_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.cancel_order_all().await;
+    println!("okx--设置持仓模式--{:?}", req_data);
+}
+
+//rest-下一个自动单
+#[tokio::test]
+async fn price_order_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+    let mut params = json!({});
+
+    params["initial"] = json!({
+        "contract": "XRP_USDT",
+        "price": "0",
+        "tif": "ioc",
+        "reduce_only": true,
+        // [平多:close_long, 平空:close_short]
+        "auto_size": "close_long"
+    });
+
+    params["trigger"] = json!({
+        // [平多:close-long-position, 平空:close-short-position]
+        "order_type": "close-long-position",
+        // 一般都默认用0
+        "strategy_type": 0,
+        // [0 - 最新成交价,1 - 标记价格,2 - 指数价格]
+        "price_type": 0,
+        // [1: 引用价格大于等于我们传的价格,2:引用价格小于等于我们传的价格]
+        // 在止损的情况下:
+        //     1 可以理解为向上突破触发价(一般是给空单用)
+        //     2 可以理解为向下突破触发价(一般是给多单用)
+        "rule": 2,
+        // 订单触发价格
+        "price": "0.5600",
+    });
+
+    let response_data = rest.place_price_order("usdt".to_string(), params).await;
+    if response_data.code == "200" {
+        let response_obj: serde_json::Value = serde_json::from_str(response_data.data.as_str()).unwrap();
+
+        info!("resp={:?}", response_obj.as_object().unwrap());
+    } else {
+        error!(?response_data);
+    }
+}
+
+#[tokio::test]
+async fn price_order_cancel_test() {
+    global::log_utils::init_log_with_info();
+
+    let mut rest = get_rest();
+
+    // 这边取消订单只能使用系统返回的
+    let rst = rest.cancel_price_order("usdt".to_string(), "58002898".to_string()).await;
+    info!(?rst);
+}
+
+//rest-查询合约账户变更历史
+#[tokio::test]
+async fn rest_account_book_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.account_book("usdt".to_string()).await;
+    println!("okx--查询合约账户变更历史--{:?}", req_data);
+}
+
+fn get_ws(btree_map: Option<GateSwapLogin>) -> GateSwapWs {
+    let binance_ws = GateSwapWs::new(false,
+                                     btree_map,
+                                     GateSwapWsType::PublicAndPrivate("usdt".to_string()));
+    binance_ws
+}
+
+
+fn get_rest() -> GateSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+
+    let ba_exc = GateSwapRest::new(false, btree_map);
+    ba_exc
+}

+ 264 - 0
exchanges/tests/kucoin_spot_test.rs

@@ -0,0 +1,264 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tracing::trace;
+
+use exchanges::kucoin_spot_rest::KucoinSpotRest;
+use exchanges::kucoin_spot_ws::{KucoinSpotLogin, KucoinSpotSubscribeType, KucoinSpotWs, KucoinSpotWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe_pu() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+
+    let mut ws = get_ws(None, KucoinSpotWsType::Public).await;
+    ws.set_symbols(vec!["BTC-USDT".to_string()]);
+    ws.set_subscribe(vec![
+        KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50,
+        KucoinSpotSubscribeType::PuMarketTicker,
+        KucoinSpotSubscribeType::PuMarketMatch,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+    });
+
+    //写数据
+    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe_pr() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let btree_map = KucoinSpotLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
+    let mut ws = get_ws(Option::from(btree_map), KucoinSpotWsType::Public).await;
+    ws.set_symbols(vec!["BTC-USDT".to_string()]);
+    ws.set_subscribe(vec![
+        KucoinSpotSubscribeType::PrAccountBalance,
+        KucoinSpotSubscribeType::PrSpotMarketTradeOrders,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+    });
+
+    //写数据
+    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+//rest-获取成交记录
+#[tokio::test]
+async fn rest_get_fills_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_fills("BTC-USDT".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  -1,
+                                  -1,
+                                  500,
+    ).await;
+    trace!(?rep_data)
+}
+
+
+//rest-获取订单
+#[tokio::test]
+async fn rest_get_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_order().await;
+    trace!(?rep_data)
+}
+
+//rest-獲取行情
+#[tokio::test]
+async fn rest_get_level1_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_level1("BTC-USDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-通過orderId获取訂單詳情
+#[tokio::test]
+async fn rest_get_order_by_order_id_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_order_by_order_id("3123123".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-通過clientOid獲取訂單詳情
+#[tokio::test]
+async fn rest_get_order_by_client_id_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_order_by_client_id("3123123".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-獲取賬戶列表 - 現貨/槓桿/現貨高頻
+#[tokio::test]
+async fn rest_get_accounts_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_accounts("USDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-獲取交易對列表
+#[tokio::test]
+async fn rest_get_symbols_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_symbols().await;
+    trace!(?rep_data)
+}
+
+//rest-通過orderId撤單- 没权限需要查看
+#[tokio::test]
+async fn rest_cancel_order_by_order_id_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_order_by_order_id("dddd123131".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-通過clientOid撤單- 没权限需要查看
+#[tokio::test]
+async fn rest_cancel_order_by_client_id_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_order_by_client_id("dddd123131".to_string()).await;
+    trace!(?rep_data)
+}
+
+
+async fn get_ws(btree_map: Option<KucoinSpotLogin>, type_v: KucoinSpotWsType) -> KucoinSpotWs {
+    let ku_ws = KucoinSpotWs::new(false, btree_map,
+                                  type_v).await;
+    ku_ws
+}
+
+fn get_rest() -> KucoinSpotRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let ku_exc = KucoinSpotRest::new(false, btree_map);
+    ku_exc
+}

+ 168 - 0
exchanges/tests/kucoin_swap_test.rs

@@ -0,0 +1,168 @@
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tracing::trace;
+
+use exchanges::kucoin_swap_ws::{KucoinSwapLogin, KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe_pu() {
+    global::log_utils::init_log_with_trace();
+
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+
+
+    let mut ws = get_ws(None, KucoinSwapWsType::Public).await;
+    ws.set_symbols(vec!["xbt_usdtM".to_string()]);
+    ws.set_subscribe(vec![
+        KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
+        KucoinSwapSubscribeType::PuContractMarketExecution,
+        KucoinSwapSubscribeType::PuContractMarkettickerV2,
+    ]);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+    });
+
+    //写数据
+    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe_pr() {
+    global::log_utils::init_log_with_trace();
+
+
+    //对象
+    let btree_map = KucoinSwapLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let mut ws = get_ws(Option::from(btree_map), KucoinSwapWsType::Private).await;
+    ws.set_symbols(vec!["xbt_usdtM".to_string()]);
+    ws.set_subscribe(vec![
+        // KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
+        // KucoinSwapSubscribeType::PuContractMarketExecution,
+        KucoinSwapSubscribeType::PuContractMarkettickerV2,
+        KucoinSwapSubscribeType::PrContractAccountWallet,
+        KucoinSwapSubscribeType::PrContractPosition,
+        KucoinSwapSubscribeType::PrContractMarketTradeOrdersSys,
+        KucoinSwapSubscribeType::PrContractMarketTradeOrders,
+    ]);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+    });
+
+    //写数据
+    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+async fn get_ws(btree_map: Option<KucoinSwapLogin>, type_v: KucoinSwapWsType) -> KucoinSwapWs {
+    let ku_ws = KucoinSwapWs::new(false, btree_map,
+                                  type_v).await;
+    ku_ws
+}

+ 128 - 0
exchanges/tests/mexc_swap_test.rs

@@ -0,0 +1,128 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use serde_json::json;
+use tokio::sync::Mutex;
+use tracing::trace;
+use exchanges::mexc_swap_rest::MexcSwapRest;
+
+use exchanges::mexc_swap_ws::{MexcSwapLogin, MexcSwapSubscribeType, MexcSwapWs, MexcSwapWsType};
+use exchanges::response_base::ResponseData;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(_)) = read_rx.try_next() {
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let fun = move |data: ResponseData| {
+        async move {
+            trace!("---传入的方法~~~~{:?}", data);
+        }
+    };
+    let param = MexcSwapLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
+    let t1 = tokio::spawn(async move {
+        let mut ws = get_ws(Option::from(param), MexcSwapWsType::PublicAndPrivate);
+        ws.set_symbols(vec!["BTC_USDT".to_string(), "ETC_USDT".to_string()]);
+        ws.set_subscribe(vec![
+            MexcSwapSubscribeType::PuFuturesTrades,
+            MexcSwapSubscribeType::PuFuturesDepth,
+            MexcSwapSubscribeType::PuFuturesRecords,
+        ]);
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+fn get_ws(btree_map: Option<MexcSwapLogin>, ws_type: MexcSwapWsType) -> MexcSwapWs {
+    let mexc_exc = MexcSwapWs::new(false,btree_map, ws_type);
+    mexc_exc
+}
+
+
+//查詢合約基礎信息
+#[tokio::test]
+async fn rest_get_market_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_market(json!({
+
+    })).await;
+    println!("mexc--查詢合約基礎信息--{:?}", req_data);
+}
+
+
+fn get_rest() -> MexcSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let mexc_exc = MexcSwapRest::new(false, btree_map.clone());
+    mexc_exc
+}
+

+ 127 - 0
exchanges/tests/okx_swap_test.rs

@@ -0,0 +1,127 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use exchanges::okx_swap_rest::OkxSwapRest;
+use serde_json::json;
+use tokio::sync::Mutex;
+use tracing::trace;
+use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+use exchanges::response_base::ResponseData;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(_)) = read_rx.try_next() {
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let fun = move |data: ResponseData| {
+        async move {
+            // trace!("---传入的方法~~~~{:?}", data);
+        }
+    };
+    let param = OkxSwapLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
+    let t1 = tokio::spawn(async move {
+        let mut ws = get_ws(Option::from(param), OkxSwapWsType::Public);
+        ws.set_symbols(vec!["ZRX_USDT".to_string(),"BTC_USDT".to_string(), "ETC_USDT".to_string()]);
+        ws.set_subscribe(vec![
+            OkxSwapSubscribeType::PuFuturesTrades,
+            OkxSwapSubscribeType::PuFuturesDepth,
+            // OkxSwapSubscribeType::BuFuturesRecords,
+
+        ]);
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+fn get_ws(btree_map: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
+    let okx_ws = OkxSwapWs::new(false,btree_map, ws_type);
+    okx_ws
+}
+
+
+
+//查詢合約基礎信息
+#[tokio::test]
+async fn rest_get_market_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_market(json!({
+
+    })).await;
+    println!("Okx--查詢合約基礎信息--{:?}", req_data);
+}
+
+fn get_rest() -> OkxSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let okx_exc = OkxSwapRest::new(false, btree_map.clone());
+    okx_exc
+}

+ 67 - 0
exchanges/tests/socket_tool_test.rs

@@ -0,0 +1,67 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use futures_util::StreamExt;
+use tokio::sync::mpsc::{channel, Sender};
+use tokio::try_join;
+use tokio_tungstenite::client_async;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::trace;
+use exchanges::binance_swap_rest::BinanceSwapRest;
+use exchanges::binance_swap_ws::{BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
+use exchanges::response_base::ResponseData;
+use exchanges::socket_tool::{client, ws_connect_async};
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    // // 币安订阅
+    let base_url = "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms".to_string();
+    // // okx 公共订阅
+    // let str = serde_json::json!({
+    //             "op": "subscribe",
+    //             "args": [
+    //                     {
+    //                     "channel":"books5",
+    //                     "instId":"BTC-USDT"
+    //                     }
+    //                 ]
+    //         });
+    //
+    // //创建通道,发送指令通道, 与读取推送数据通道
+    // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+    //
+    //
+    // //对象
+    // // let model = OkxSwapModel::new();
+    // let is_ok =  ws_connect_async(base_url).await;
+    // //写数据,订阅频道,有些是Url跟参数,有些是单独发送订阅字符串
+    // // write_tx.unbounded_send(Message::Text(str.to_string())).expect("发送失败");
+    // match is_ok {
+    //     Ok(_) => {
+    //         let t1 = tokio::spawn(async move {
+    //             loop {
+    //                 if let Some(message) = read_rx.next().await {
+    //
+    //                 }
+    //             }
+    //         });
+    //         try_join!(t1).unwrap();
+    //     }
+    //     _ => {}
+    // }
+    client(base_url).await;
+}
+
+//
+// fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> BinanceSwapWs {
+//     let binance_ws = BinanceSwapWs::new(false,
+//                                         btree_map,
+//                                         BinanceSwapWsType::PublicAndPrivate);
+//     binance_ws
+// }

+ 547 - 0
exchanges/tests/test.rs

@@ -0,0 +1,547 @@
+use exchanges::gate_swap_rest::GateSwapRest;
+use std::collections::BTreeMap;
+use tokio::io::{AsyncReadExt};
+use exchanges::kucoin_swap_rest::KucoinSwapRest;
+use exchanges::kucoin_swap_ws::{KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
+use exchanges::{proxy};
+use exchanges::okx_swap_ws::{OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+
+use std::io::{Read, Write};
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use tokio::sync::mpsc::channel;
+use tokio::try_join;
+use tracing::{trace};
+use tracing::instrument::WithSubscriber;
+use exchanges::binance_swap_rest::BinanceSwapRest;
+use exchanges::binance_swap_ws::{BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
+use exchanges::okx_swap_rest::OkxSwapRest;
+
+#[tokio::test]
+async fn test_import() {
+    global::log_utils::init_log_with_trace();
+
+
+    /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
+    if proxy::ParsingDetail::http_enable_proxy() {
+        trace!("检测有代理配置,配置走代理");
+    }
+
+
+    //获取代理
+    // demo_get_http_proxy();
+
+    //币安---深度socket-公共频道订阅
+    // demo_pub_ws_ba().await;
+    // 币安-rest-获取账户信息
+    // demo_rest_ba().await;
+    //本次更新成功
+
+
+    //gate-rest -账户信息
+    // demo_rest_gate().await;
+    //gate-ws-public-private频道
+    // demo_ws_gate().await;
+
+
+    //kucoin_rest -账户信息
+    // demo_rest_kucoin().await;
+    //Kucoin-ws--公共频道
+    // demo_ws_kucoin_pu().await;
+    //Kucoin-ws--私有频道
+    // demo_ws_kucoin_pr().await;
+
+    //okx - Business 频道
+    // demo_ws_okx_bu().await;
+    //okx - public 频道
+    // demo_ws_okx_pu().await;
+    //okx - rest 频道
+    // demo_okx_rest().await;
+
+    // demo_so();
+
+
+    // let mut ku_ws = KucoinSwapWs::new(false, btree_map.clone(),
+    //                                   KucoinWsType::Private, tx).await;
+    // ku_ws.set_subscribe(vec![KucoinSubscribeType::PrContractMarketTradeOrdersSys]);
+    //
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
+    // });
+    //
+    // let t2 = tokio::spawn(async move {
+    //     let mut stdout = std::io::stdout();
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             writeln!(stdout, "age: {:?}", received).unwrap();
+    //         }
+    //     }
+    // let t01 = tokio::spawn(async move {
+    //     loop {
+    //         tokio::time::sleep(Duration::from_secs(2)).await;
+    //         trace!( "发送-指令: ");
+    //     }
+    // });
+    // let t02 = tokio::spawn(async move {
+    //     loop {
+    //         tokio::time::sleep(Duration::from_secs(3)).await;
+    //         trace!( "接收 -指令: ");
+    //     }
+    // });
+    // try_join!(t01,t02).unwrap();
+
+
+    // let (mut tx_end, mut rx_end) = channel::<String>(1024);
+    // let (mut tx_read, mut rx_read) = channel::<String>(1024);
+    // let mut stream = tokio::net::TcpStream::connect("127.0.0.1:8080").await.unwrap();
+    // stream.write_all( b"Hello, server!").await.unwrap();
+    // stream.flush().await.unwrap();
+    // let mutex_stream = Arc::new(Mutex::new(stream));
+    //
+    // //捕捉用户的主动发送的订阅指令
+    // let stream_clone = Arc::clone(&mutex_stream);
+    // let t_1 = tokio::spawn(async move {
+    //     loop {
+    //         tokio::time::sleep(Duration::from_secs(1)).await;
+    //         if let Ok(received) = rx_end.try_recv() {
+    //             trace!("动态订阅内容: {:?}", received);
+    //             let mut stream_lock = stream_clone.lock().await;
+    //             // stream_lock.write_all( b"1111!").await.unwrap();
+    //             // stream_lock.flush().await.unwrap();
+    //
+    //             // stream_lock.write_all(b"Hello, server!").await.unwrap();
+    //         }
+    //     }
+    // });
+    //
+    //
+    // //socket数据获取,装入回显通道
+    // let stream_clone = Arc::clone(&mutex_stream);
+    // let t_2 = tokio::spawn(async move {
+    //     // 创建一个用于存储服务器响应的缓冲区
+    //     let mut buffer = [0; 512];
+    //     loop {
+    //         let mut stream_lock = stream_clone.lock().await;
+    //         tokio::time::sleep(Duration::from_secs(1)).await;
+    //
+    //         let _ = match stream_lock.read(&mut buffer).await {
+    //             Ok(n) => {
+    //                 tokio::time::sleep(Duration::from_secs(1)).await;
+    //                 if n == 0 {
+    //                     // 没有读取到数据
+    //                     trace!("没有数据,主动发送");
+    //                 } else {
+    //                     // 打印服务器的响应
+    //                     trace!("有数据: {}", String::from_utf8_lossy(&buffer[..n]));
+    //                     tx_read.send(format!("{}", String::from_utf8_lossy(&buffer[..n]))).await.unwrap()
+    //                 }
+    //             }
+    //             Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+    //                 // 如果读取操作会阻塞,则等待一会儿再试
+    //                 trace!("Would block, sleeping会阻碍睡眠??");
+    //                 tokio::time::sleep(Duration::from_secs(3)).await;
+    //                 continue;
+    //             }
+    //             Err(e) => {
+    //                 trace!("Err:{:?}",e);
+    //                 break;
+    //             }
+    //         };
+    //     }
+    // });
+    //
+    //
+    // //socket 数据回显
+    // let t02 = tokio::spawn(async move {
+    //     loop {
+    //         tokio::time::sleep(Duration::from_secs(1)).await;
+    //         // tx.send("hai!!!".to_string()).await.unwrap();
+    //         if let Ok(received) = rx_read.try_recv() {
+    //             trace!("拿到 socket 的数据: {:?}", received);
+    //         }
+    //     }
+    // });
+    //
+    // //模拟用户动态发送
+    // let t03 = tokio::spawn(async move {
+    //     loop {
+    //         tokio::time::sleep(Duration::from_secs(1)).await;
+    //         tx_end.send("这里是 动态订阅".to_string()).await.unwrap();
+    //     }
+    // });
+    //
+    // try_join!(t_1,t_2,t02,t03).unwrap();
+}
+
+
+async fn demo_okx_rest() {
+    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // btree_map.insert("access_key".to_string(), "".to_string());
+    // btree_map.insert("secret_key".to_string(), "".to_string());
+    // btree_map.insert("pass_key".to_string(), "".to_string());
+    //
+    // let mut okx_rest = OkxSwapRest::new(false, btree_map);
+    // let res_data = okx_rest.get_account("BTC".to_string()).await;
+    // trace!("okx_rest-rest - get_account- {:?}", res_data);
+}
+
+async fn demo_ws_gate() {}
+
+
+fn demo_so() {
+    // 代理服务器地址和端口
+    // let proxy_address = "127.0.0.1:7890";
+    // // 目标服务器地址和端口
+    // let target_address = "wss://ws.okx.com:8443/ws/v5/public";
+    //
+    //   // 建立与代理服务器的连接
+    //   let mut proxy_stream = TcpStream::connect(proxy_address).expect("Failed to connect to proxy");
+    //
+    //   // 发送代理请求
+    //   let request = format!("CONNECT {}\r\n\r\n", target_address);
+    //   proxy_stream.write_all(request.as_bytes()).expect("Failed to send proxy request");
+    //
+    //   // 读取代理响应
+    //   let mut response = String::new();
+    //   proxy_stream.read_to_string(&mut response).expect("Failed to read proxy response");
+    //
+    //   // 检查代理响应是否成功
+    //   if !response.contains("200 Connection established") {
+    //       trace!("Proxy connection failed: {}", response);
+    //   }
+    //
+    //   // 从代理连接中获取原始 TCP 流
+    //   let mut tcp_stream = std::mem::ManuallyDrop::new(proxy_stream);
+    //
+    //   // 现在你可以使用 `tcp_stream` 来进行与目标服务器的通信
+    //   // 例如,发送和接收数据
+    //   // tcp_stream.write_all(b"Hello, server!").expect("Failed to send data");
+    //
+    //   thread::spawn(move || {
+    //       loop {
+    //           let mut buffer = [0u8; 1024]; // 用于存储读取的数据的缓冲区
+    //           let bytes_read = tcp_stream.read(&mut buffer).expect("Failed to read data");
+    //
+    //           // 将读取的数据转换为字符串并打印
+    //           if let Ok(data) = std::str::from_utf8(&buffer[..bytes_read]) {
+    //               trace!("Received data: {}", data);
+    //           } else {
+    //               trace!("Received data contains non-UTF8 characters");
+    //           }
+    //       }
+    //
+    //   });
+    //
+    //   // 最后记得手动释放套接字
+    //   // 注意:不要调用 `drop(tcp_stream)`,因为我们使用了 `ManuallyDrop` 来避免自动释放
+    //   unsafe {
+    //       std::mem::ManuallyDrop::drop(&mut tcp_stream);
+    //   }
+}
+
+
+async fn demo_ws_okx_pu() {
+
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Public, tx);
+    // ku_ws.set_subscribe(vec![OkxSubscribeType::PuIndexTickers]);
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     let mut stdout = std::io::stdout();
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             writeln!(stdout, "age: {:?}", received).unwrap();
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
+}
+
+async fn demo_ws_okx_bu() {
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Business, tx);
+    // ku_ws.set_subscribe(vec![OkxSubscribeType::BuIndexCandle30m]);
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     let mut stdout = std::io::stdout();
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             writeln!(stdout, "age: {:?}", received).unwrap();
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
+}
+
+async fn demo_ws_kucoin_pr() {
+    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
+    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // btree_map.insert("access_key".to_string(), "".to_string());
+    // btree_map.insert("secret_key".to_string(), "".to_string());
+    // btree_map.insert("pass_key".to_string(), "".to_string());
+    // trace!("----------------------btree_map{:?}", btree_map.clone());
+    // let (tx, mut rx) = channel(1024);
+    // let mut ku_ws = KucoinSwapWs::new(false, btree_map.clone(),
+    //                                   KucoinWsType::Private, tx).await;
+    // ku_ws.set_subscribe(vec![KucoinSubscribeType::PrContractMarketTradeOrdersSys]);
+    //
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(is_shutdown_arc, vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
+    // });
+    //
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             trace!( "age: {:?}", received);
+    //         }
+    //     }
+    // });
+    //
+    // try_join!(t1,t2).unwrap();
+}
+
+async fn demo_ws_kucoin_pu() {
+    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut ku_ws = KucoinSwapWs::new(false, btree_map, KucoinWsType::Public, tx).await;
+    // ku_ws.set_subscribe(vec![
+    //     KucoinSubscribeType::PuContractMarketLevel2Depth50,
+    //     KucoinSubscribeType::PuContractMarkettickerV2,
+    // ]);
+    //
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(is_shutdown_arc, vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             trace!("age: {:?}", received);
+    //         }
+    //     }
+    // });
+    //
+    // try_join!(t1,t2).unwrap();
+}
+
+async fn demo_rest_kucoin() {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), "".to_string());
+    btree_map.insert("secret_key".to_string(), "".to_string());
+    btree_map.insert("pass_key".to_string(), "".to_string());
+
+    let mut kucoin_exc = KucoinSwapRest::new(false, btree_map);
+    // let res_data = kucoin_exc.get_server_time().await;
+    // trace!("kucoin_exc-rest - get_server_time- {:?}", res_data);
+    // trace!("kucoin_exc-rest-get_delays{:?}", kucoin_exc.get_delays() );
+    // trace!("kucoin_exc-rest -get_avg_delay{:?}", kucoin_exc.get_avg_delay());
+    // let res_data = kucoin_exc.get_account("USDT".to_string()).await;
+    // trace!("kucoin_exc-rest - get_account- {:?}", res_data);
+    // let res_data = kucoin_exc.get_position("XBT1USDM".to_string()).await;
+    // trace!("kucoin_exc-rest - get_position- {:?}", res_data);
+    // let res_data = kucoin_exc.get_market_details().await;
+    // trace!("kucoin_exc-rest - get_market_details- {:?}", res_data);
+    // let res_data = kucoin_exc.get_ticker("ROSEUSDTM".to_string()).await;
+    // trace!("kucoin_exc-rest - get_ticker- {:?}", res_data);
+    let res_data = kucoin_exc.get_orders("".to_string(),
+                                         "".to_string()).await;
+    trace!("kucoin_exc-rest - get_orders- {:?}", res_data);
+    // let res_data = kucoin_exc.get_positions("USDT".to_string()).await;
+    // trace!("kucoin_exc-rest - get_positions- {:?}", res_data);
+    // let res_data = kucoin_exc.get_orders_details("111".to_string(), "".to_string()).await;
+    // trace!("kucoin_exc-rest - get_orders_details- {:?}", res_data);
+    // let res_data = kucoin_exc.swap_bazaar_order(
+    //     "cs_202309111808".to_string(),
+    //     "ROSEUSDTM".to_string(),
+    //     "pd".to_string(),
+    //     1,
+    //     "10".to_string(),
+    //     "0.03856".to_string(),
+    //     "limit".to_string(),
+    // ).await;
+    // trace!("kucoin_exc-rest - swap_bazaar_order- {:?}
+    // let res_data = kucoin_exc.cancel_order("".to_string(), "999999".to_string()).await;
+    // trace!("kucoin_exc-rest - cancel_order- {:?}", res_data);
+    // let res_data = kucoin_exc.get_public_token().await;
+    // trace!("kucoin_exc-rest - get_public_token- {:?}", res_data);
+}
+
+async fn demo_rest_gate() {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), "".to_string());
+    btree_map.insert("secret_key".to_string(), "".to_string());
+
+    let mut gate_exc = GateSwapRest::new(false, btree_map);
+    let res_data = gate_exc.get_account("usdt".to_string()).await;
+    trace!("gate-rest -账户信息{:?}", res_data);
+    trace!("gate-rest -get_delays{:?}", gate_exc.get_delays() );
+    trace!("gate-rest -get_avg_delay{:?}", gate_exc.get_avg_delay());
+    // let res_data = gate_exc.get_position("usdt".to_string(), "CYBER_USDT".to_string()).await;
+    // trace!("gate-rest -持仓信息{:?}", res_data);
+    // let res_data = gate_exc.get_ticker("usdt".to_string()).await;
+    // trace!("gate-rest -ticker{:?}", res_data);
+    // let res_data = gate_exc.get_server_time().await;
+    // trace!("gate-rest -get_server_time{:?}", res_data);
+    // let res_data = gate_exc.get_user_position("usdt".to_string()).await;
+    // trace!("gate-rest -get_server_time{:?}", res_data);
+    // let res_data = gate_exc.get_order_details("usdt".to_string(), "11335522".to_string()).await;
+    // trace!("gate-rest -get_order_details{:?}", res_data);
+    // let res_data = gate_exc.get_orders("usd1t".to_string(), "open".to_string()).await;
+    // trace!("gate-rest -get_orders{:?}", res_data);
+    // let params = serde_json::json!({
+    //         "contract":"CYBER_USDT",
+    //         "size":-1,
+    //         "price":"0",
+    //         "tif":"ioc",
+    //      });
+    // let res_data = gate_exc.take_order("usdt".to_string(), params).await;
+    // trace!("gate-rest -get_orders{:?}", res_data);
+    // let res_data = gate_exc.setting_dual_mode("usdt".to_string(), true).await;
+    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
+    // let res_data = gate_exc.setting_dual_leverage("usdt".to_string(),
+    //                                               "CYBER_USDT".to_string(),
+    //                                               "20".to_string(),
+    // ).await;
+    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
+    // let res_data = gate_exc.wallet_transfers("usdt".to_string(),
+    //                                               "CYBER_USDT".to_string(),
+    //                                               "20".to_string(),
+    // ).await;
+    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
+    // let res_data = gate_exc.cancel_order("usdt".to_string(),
+    //                                      "12345".to_string(),
+    // ).await;
+    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
+    // let res_data = gate_exc.cancel_orders("usdt".to_string(),
+    //                                      "CYBER_USDT".to_string(),
+    // ).await;
+    // trace!("gate-rest -cancel_orders{:?}", res_data);
+    // let res_data = gate_exc.order(
+    //     "usdt".to_string(),
+    //     "long".to_string(),
+    //     "buy".to_string(),
+    //     "ROSE_USDT".to_string(),
+    //     1,
+    //     "0.03888".to_string(),
+    //     "t-my-custom-id-001".to_string(),
+    // ).await;
+    // trace!("gate-rest -order{:?}", res_data);
+
+
+    // let res_data = gate_exc.my_trades("usdt".to_string()).await;
+    // trace!("gate-rest -my_trades{:?}", res_data);
+    let res_data = gate_exc.account_book("usdt".to_string()).await;
+    trace!("gate-rest -account_book{:?}", res_data);
+}
+
+async fn demo_rest_ba() {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), "".to_string());
+    btree_map.insert("secret_key".to_string(), "".to_string());
+
+    // let ba_exc = BinanceUsdtSwapRest::new(false, btree_map);
+    // let res_data = ba_exc.get_account().await;
+    let mut ba_exc = BinanceSwapRest::new(false, btree_map);
+    // let res_data = ba_exc.get_server_time().await;
+    // trace!("币安-rest - get_server_time{:?}", res_data);
+    // let res_data = ba_exc.get_exchange_info().await;
+    // trace!("币安-restget_ get_exchange_info{:?}", res_data);
+
+
+    let res_data = ba_exc.get_account().await;
+    trace!("币安-rest-获取账户信息{:?}", res_data);
+    // trace!("币安-rest- -get_delays{:?}", ba_exc.get_delays() );
+    // trace!("币安-rest--get_avg_delay{:?}", ba_exc.get_avg_delay());
+
+    // let res_data = ba_exc.get_order("BTCUSDT".to_string(), 131, "".to_string()).await;
+    // trace!("币安-rest--get_order{:?}", res_data);
+    // let res_data = ba_exc.get_open_orders("BTCUSDT".to_string()).await;
+    // trace!("币安-rest--get_open_orders{:?}", res_data);
+    // let timestamp_start = chrono::Utc::now().timestamp_millis() - 60 * 100 * 1000;
+    // let timestamp_end = chrono::Utc::now().timestamp_millis() +  60 * 100 * 1000;
+    // let res_data = ba_exc.get_all_orders("BTCUSDT".to_string(), 1000, timestamp_start, timestamp_end).await;
+    // trace!("币安-rest--get_all_orders{:?}", res_data);
+    // let res_data = ba_exc.get_book_ticker("BTCUSDT".to_string()).await;
+    // trace!("币安-rest--get_book_ticker{:?}", res_data);
+    // let res_data = ba_exc.get_position_risk("BTCUS1DT".to_string()).await;
+    // trace!("币安-rest--get_position_risk{:?}", res_data);
+    // let res_data = ba_exc.change_pos_side(true).await;
+    // trace!("币安-rest--change_pos_side{:?}", res_data);
+    // let res_data = ba_exc.cancel_order("BTCUSDT".to_string(), 3312331, "".to_string()).await;
+    // trace!("币安-rest--cancel_order{:?}", res_data);
+}
+
+async fn demo_pub_ws_ba() {
+    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut binance_ws = BinanceSwapWs::new(false, btree_map, BinanceWsType::PublicAndPrivate, tx);
+    // binance_ws.set_subscribe(vec![
+    //     BinanceSubscribeType::PuAggTrade,
+    //     BinanceSubscribeType::PuDepth20levels100ms,
+    //     BinanceSubscribeType::PuBookTicker,
+    // ]);
+    // let t1 = tokio::spawn(async move {
+    //     binance_ws.custom_subscribe(is_shutdown_arc,vec!["BTCUSDT".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             trace!( "age: {:?}", received);
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
+}
+
+fn demo_get_http_proxy() {
+    //代理地址
+    let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
+    trace!("----代理信息:{:?}", parsing_detail);
+}
+
+
+// /*********************web服务*/
+// fn demo_http() {
+//     let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
+//     for stream in listener.incoming() {
+//         let stream = stream.unwrap();
+//
+//         handle_connection(TcpStream::try_from(stream).unwrap());
+//     }
+// }
+//
+//
+// fn handle_connection(mut stream: TcpStream) {
+//     let buf_reader = BufReader::new(&mut stream);
+//     let http_request: Vec<_> = buf_reader
+//         .lines()
+//         .map(|result| result.unwrap())
+//         .take_while(|line| !line.is_empty())
+//         .collect();
+//     trace!("Request: {:#?}", http_request);
+//     trace!("Request2: {:#?}", http_request[0]);
+//     trace!("Request3: {:#?}", http_request[1]);
+//
+//     let (status_line, filename) = if http_request[0] == "GET / HTTP/1.1" {
+//         ("HTTP/1.1 200 OK", "hello.html")
+//     } else {
+//         ("HTTP/1.1 404 NOT FOUND", "404.html")
+//     };
+//
+//     let status_line = "HTTP/1.1 200 OK";
+//     let contents = fs::read_to_string("src/404.html").unwrap();
+//     let length = contents.len();
+//
+//     let response =
+//         format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
+//     // let response = "HTTP/1.1 200 OK\r\n\r\nccccc";
+//
+//     stream.write_all(response.as_bytes()).unwrap();
+// }

+ 140 - 0
exchanges/tests/woo_swap_test.rs

@@ -0,0 +1,140 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+use serde_json::json;
+use tokio::sync::Mutex;
+use tracing::trace;
+use exchanges::response_base::ResponseData;
+use exchanges::woo_swap_rest::WooSwapRest;
+use exchanges::woo_swap_ws::{WooSwapLogin, WooSwapSubscribeType, WooSwapWs, WooSwapWsType};
+
+const APPLICATION_ID: &str = "4bd2d1a1-c033-43a1-b977-1aefa754e715";
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe() {
+    global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            // 从通道中接收并丢弃所有的消息,直到通道为空
+            while let Ok(Some(_)) = read_rx.try_next() {
+
+                // 从通道中接收并丢弃所有的消息,直到通道为空
+                while let Ok(Some(_)) = read_rx.try_next() {
+                    // 消息被忽略
+                }
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let fun = move |data: ResponseData| {
+        async move {
+            trace!("---传入的方法~~~~{:?}", data);
+        }
+    };
+    let param = WooSwapLogin {
+        api_key: "".to_string(),
+        secret: "".to_string(),
+        api_memo: "".to_string(),
+    };
+    let t1 = tokio::spawn(async move {
+        let mut ws = get_ws(Option::from(param), WooSwapWsType::Public(APPLICATION_ID.to_string()));
+        ws.set_symbols(vec!["BTC_USDT".to_string(),"ETC_USDT".to_string()]);
+        ws.set_subscribe(vec![
+            WooSwapSubscribeType::PuFuturesTrades,
+            WooSwapSubscribeType::PuFuturesDepth,
+            WooSwapSubscribeType::PuFuturesRecords,
+        ]);
+        //链接
+        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+fn get_ws(btree_map: Option<WooSwapLogin>, ws_type: WooSwapWsType) -> WooSwapWs {
+    let Woo_ws = WooSwapWs::new(false, btree_map, ws_type);
+    Woo_ws
+}
+
+
+//查询服务器时间是
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_server_time().await;
+    println!("Woo--查询服务器时间是--{:?}", req_data);
+}
+
+
+//查詢合約基礎信息
+#[tokio::test]
+async fn rest_get_market_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_market(json!({
+
+    })).await;
+    println!("Woo--查詢合約基礎信息--{:?}", req_data);
+}
+
+
+fn get_rest() -> WooSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let woo_exc = WooSwapRest::new(false, btree_map.clone());
+    woo_exc
+}
+

+ 5 - 0
global/.gitignore

@@ -0,0 +1,5 @@
+/target
+/.idea
+/logs
+
+Cargo.lock

+ 25 - 0
global/Cargo.toml

@@ -0,0 +1,25 @@
+[package]
+name = "global"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rust_decimal = "1.32.0"
+rust_decimal_macros = "1.32.0"
+tracing = "0.1"
+tracing-subscriber = { version = "0.3.17", features = [
+    "env-filter",
+    "time",
+    "local-time"
+] }
+time = { version = "0.3.7", features = ["macros"] }
+tracing-appender-timezone = { git = "https://github.com/skyfffire/tracing-appender-timezone.git" }
+serde = "1.0.183"
+serde_derive = "1.0"
+serde_json = "1.0.104"
+chrono = "0.4.26"
+tokio = { version = "1.31.0", features = ["full"] }
+uuid = { version = "1.5.0", features = ["v4"] }
+simple_excel_writer = "0.2.0"

+ 1 - 0
global/src/lib.rs

@@ -0,0 +1 @@
+pub mod log_utils;

+ 105 - 0
global/src/log_utils.rs

@@ -0,0 +1,105 @@
+use std::fmt::Debug;
+use std::io;
+use tracing::{Event, Subscriber, warn};
+use tracing_appender_timezone::non_blocking::WorkerGuard;
+use tracing_subscriber::{fmt, Layer};
+use tracing_subscriber::layer::{Context, SubscriberExt};
+use tracing::field::{Field, Visit};
+use tracing_appender_timezone::rolling::{RollingFileAppender, Rotation};
+
+
+struct ErrorMessageVisitor {
+    message: String
+}
+
+impl Visit for ErrorMessageVisitor {
+    fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
+        if field.name() == "message" {
+            self.message = format!("{:?}", value);
+        }
+    }
+}
+
+
+// 错误报告发送到指定服务器
+struct ReportingLayer {
+    app_name: String,
+}
+impl<S> Layer<S> for ReportingLayer
+    where
+        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
+{
+    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
+        if event.metadata().level() == &tracing::Level::ERROR {
+            let mut visitor = ErrorMessageVisitor {
+                message: String::new()
+            };
+            event.record(&mut visitor);
+
+            warn!("Unhandle error: {}", self.app_name)
+        }
+    }
+}
+
+pub fn init_log_with_debug() {
+    let _ = final_init(tracing::Level::DEBUG.as_str(), 0, "test".to_string());
+}
+
+pub fn init_log_with_trace() {
+    let _ = final_init(tracing::Level::TRACE.as_str(), 0, "test".to_string());
+}
+
+pub fn init_log_with_info() {
+    let _ = final_init(tracing::Level::INFO.as_str(), 0, "test".to_string());
+}
+
+pub fn final_init(level: &str, _port: u32, app_name: String) -> WorkerGuard {
+    let mut path = String::new();
+    path.push_str("./logs");
+
+    let file_appender = RollingFileAppender::builder()
+        .time_zone(8)
+        .rotation(Rotation::DAILY)
+        .filename_suffix("log")
+        .build(path)
+        .expect("initializing rolling file appender failed");
+    let (non_blocking, guard) = tracing_appender_timezone::non_blocking(file_appender);
+
+    use time::{macros::format_description, UtcOffset};
+    use tracing_subscriber::{fmt::time::OffsetTime};
+    let local_time = OffsetTime::new(
+        UtcOffset::from_hms(8, 0, 0).unwrap(),
+        format_description!("[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]"),
+    );
+
+    let fmt_layer = fmt::layer()
+        .with_timer(local_time.clone())
+        .with_line_number(true)
+        .with_target(true)
+        .with_level(true)
+        .with_writer(io::stdout)
+        .with_span_events(fmt::format::FmtSpan::FULL);
+
+    let file_layer = fmt::layer()
+        .with_timer(local_time.clone())
+        .with_line_number(true)
+        .with_target(true)
+        .with_ansi(false)
+        .with_level(true)
+        .with_writer(non_blocking.clone())
+        .with_span_events(fmt::format::FmtSpan::FULL);
+
+    let reporting_layer = ReportingLayer {
+        app_name
+    };
+
+    let layer = tracing_subscriber::Registry::default()
+        .with(fmt_layer)
+        .with(file_layer)
+        .with(reporting_layer)
+        .with(tracing_subscriber::EnvFilter::new(level));
+
+    tracing::subscriber::set_global_default(layer).unwrap();
+
+    return guard;
+}

+ 12 - 0
src/control_c.rs

@@ -0,0 +1,12 @@
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use tracing::info;
+
+pub fn exit_handler(running: Arc<AtomicBool>) {
+    let r = running.clone();
+    ctrlc::set_handler(move || {
+        info!("检测到退出信号!");
+        r.store(false, Ordering::Relaxed);
+    })
+    .expect("Error setting Ctrl-C handler");
+}

+ 109 - 0
src/gate_usdt_swap_data_listener.rs

@@ -0,0 +1,109 @@
+use std::collections::{BTreeMap, HashMap};
+use std::str::FromStr;
+use std::sync::{Arc};
+use std::sync::atomic::{AtomicBool};
+use std::time::Duration;
+use chrono::Utc;
+use lazy_static::lazy_static;
+use rust_decimal::Decimal;
+use tokio::sync::{Mutex};
+use tracing::info;
+use exchanges::gate_swap_rest::GateSwapRest;
+use exchanges::gate_swap_ws::{GateSwapSubscribeType, GateSwapWs, GateSwapWsType};
+use exchanges::response_base::ResponseData;
+use rust_decimal_macros::dec;
+use standard::exchange::ExchangeEnum;
+use standard::exchange_struct_handler::ExchangeStructHandler;
+use crate::json_db_utils::collect_special_trades_json;
+use crate::listener_tools::{TradeMap, update_trade};
+use crate::msv::{generate_msv_by_trades, parse_json_to_trades};
+
+const EXCHANGE_NAME: &str = "gate_usdt_swap";
+
+lazy_static! {
+    static ref TRADES_MAP: Mutex<TradeMap> = Mutex::new(HashMap::new());
+    static ref MUL_MAP: Mutex<HashMap<String, Decimal>> = Mutex::new(HashMap::new());
+}
+
+pub async fn run_listener(is_shutdown_arc: Arc<AtomicBool>) {
+    let name = "gate_usdt_swap_listener";
+    // 订阅所有币种
+    let login = BTreeMap::new();
+    let mut gate_rest = GateSwapRest::new(false, login);
+    let response = gate_rest.get_market_details("usdt".to_string()).await;
+    let mut symbols = vec![];
+    if response.code == 200 {
+        let symbol_infos = response.data.as_array().unwrap();
+        let mut mul_map = MUL_MAP.lock().await;
+        for symbol_info in symbol_infos {
+            // quanto_multiplier是ct_val
+            let symbol = symbol_info["name"].as_str().unwrap().to_string();
+            let mul = Decimal::from_str(symbol_info["quanto_multiplier"].as_str().unwrap().to_string().as_str()).unwrap();
+            mul_map.insert(symbol.clone(), mul);
+
+            symbols.push(symbol)
+        }
+    }
+
+    for chunk in symbols.chunks(20) {
+        let ws_name = name.to_string();
+        let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        let write_tx_am = Arc::new(Mutex::new(write_tx));
+        let symbols_chunk = chunk.iter().cloned().collect::<Vec<String>>();
+        let is_shutdown_clone = Arc::clone(&is_shutdown_arc);
+
+        tokio::spawn(async move {
+            let mut ws = GateSwapWs::new_with_tag(ws_name, false, None, GateSwapWsType::PublicAndPrivate("usdt".to_string()));
+            ws.set_subscribe(vec![
+                GateSwapSubscribeType::PuFuturesTrades,
+            ]);
+
+            // 建立链接
+            ws.set_symbols(symbols_chunk);
+            ws.ws_connect_async(is_shutdown_clone, data_listener, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        });
+    }
+
+    // 每分钟计算msv
+    loop {
+        let mut indicators = BTreeMap::new();
+        let end_timestamp = Utc::now().timestamp_millis();
+        let start_timestamp = end_timestamp - 60 * 1000 * 60;
+        for symbol in symbols.clone() {
+            let trades_value = collect_special_trades_json(start_timestamp, end_timestamp, "gate_usdt_swap", &symbol).await;
+            let trades = parse_json_to_trades(trades_value);
+            let msv = generate_msv_by_trades(trades, dec!(50), vec![], start_timestamp, end_timestamp);
+            indicators.insert(symbol, msv);
+        }
+        tokio::time::sleep(Duration::from_secs(60)).await;
+    }
+}
+
+// 读取数据
+pub async fn data_listener(response: ResponseData) {
+    if response.code != 200 {
+        return;
+    }
+    match response.channel.as_str() {
+        // 订单流数据
+        "futures.trades" => {
+            let mut trades = ExchangeStructHandler::trades_handle(ExchangeEnum::GateSwap, &response);
+            let mul_map = MUL_MAP.lock().await;
+
+            for trade in trades.iter_mut() {
+                // 真实交易量处理,因为gate的量都是张数
+                let mul = mul_map[trade.symbol.as_str()];
+                let mut real_size = trade.size * mul * trade.price;
+                real_size.rescale(2);
+                trade.size = real_size;
+
+                // 更新到本地数据库
+                let trades_map = TRADES_MAP.lock().await;
+                update_trade(trade, trades_map, EXCHANGE_NAME).await;
+            }
+        }
+        _ => {
+            info!("48 未知的数据类型: {:?}", response)
+        }
+    }
+}

+ 185 - 0
src/json_db_utils.rs

@@ -0,0 +1,185 @@
+use std::path::{Path, PathBuf};
+use chrono::{FixedOffset, TimeZone, Utc};
+use serde_json::Value;
+// use serde_json::Value;
+use tokio::fs::File;
+use tokio::io::AsyncWriteExt;
+use tokio::{fs};
+// use tracing::{error, info};
+use tracing::{error};
+use standard::{SpecialTrade};
+
+pub async fn write_to_file(json_data: String, file_path: String) {
+    // 尝试创建文件路径
+    if let Err(e) = fs::create_dir_all(
+        // 获取文件目录路径
+        Path::new(&file_path)
+            .parent() // 获取父目录(即文件路径除去文件名后的部分)
+            .unwrap_or_else(|| Path::new("")), // 如果没有父目录,使用当前目录
+    )
+        .await
+    {
+        // 如果创建路径失败,打印错误日志
+        error!("创建目录错误: {:?}", e);
+        return; // 结束任务
+    }
+
+    // 异步地执行文件写入操作
+    if let Err(e) = async {
+        let mut file = File::create(&file_path).await?;
+        file.write_all(json_data.as_bytes()).await?;
+        Result::<(), std::io::Error>::Ok(())
+    }
+        .await
+    {
+        // 如果发生错误,只打印错误日志
+        error!("json db写入错误: {:?}", e);
+    }
+}
+
+// 根据时间戳生成文件名列表
+fn generate_filenames(start_timestamp: i64, end_timestamp: i64, exchange: &str, symbol: &str, subscriber_type: &str) -> Vec<String> {
+    let mut filenames = Vec::new();
+    let start_minute = start_timestamp / 60000; // 转换为分钟
+    let end_minute = end_timestamp / 60000; // 转换为分钟
+
+    let mut minute = end_minute;
+    while minute >= start_minute {
+        let date_str = minute_to_date(minute);
+        filenames.push(generate_file_path(exchange, date_str.as_str(), symbol, subscriber_type, minute));
+
+        minute -= 1;
+    }
+
+    filenames
+}
+
+pub fn generate_file_path(exchange: &str, formatted_date: &str, symbol: &str, subscriber_type: &str, serial: i64) -> String {
+    return format!("db/{}/{}/{}/{}/{}.json", exchange, formatted_date, symbol, subscriber_type, serial);
+}
+
+// 函数:将分钟数转换为日期字符串,格式为 YYYYMMDD
+pub fn minute_to_date(minute: i64) -> String {
+    // 将分钟转换为秒
+    let seconds = minute * 60;
+
+    // 创建一个代表东八区(GMT+8)的时间偏移
+    let east_eight_zone = FixedOffset::east_opt(8 * 3600).unwrap();
+
+    // 使用 UTC 时间创建 DateTime 对象,然后将其转换为东八区时间
+    let datetime_utc = Utc.timestamp_opt(seconds, 0).unwrap();
+    let datetime_east_eight = datetime_utc.with_timezone(&east_eight_zone);
+
+    // 返回日期字符串(格式 YYYYMMDD)
+    datetime_east_eight.format("%Y%m%d").to_string()
+}
+
+// 将一个时间段范围内的所有SpecialTrade返回(以json形式)
+pub async fn collect_special_trades_json(start_timestamp: i64, end_timestamp: i64, exchange: &str, symbol: &str) -> Value {
+    let mut all_trades = Vec::new();
+    let filenames = generate_filenames(start_timestamp, end_timestamp, exchange, symbol, "trades");
+
+    for filename in filenames {
+        let file_path = PathBuf::from(filename.as_str());
+        let file_content = fs::read_to_string(file_path).await;
+
+        // 检查文件内容是否成功读取
+        let mut trades = if let Ok(content) = file_content {
+            // 尝试反序列化文件内容
+            if let Ok(trades) = serde_json::from_str::<Vec<SpecialTrade>>(&content) {
+                trades // 成功反序列化,返回结果
+            } else {
+                vec![] // 反序列化失败,返回空 Vec
+            }
+        } else {
+            vec![] // 读取文件失败,返回空 Vec
+        };
+
+        trades.reverse();
+        // info!("{} 找到 {} 条", filename, trades.len());
+        all_trades.append(&mut trades);
+    }
+
+    serde_json::to_value(&all_trades).unwrap()
+}
+
+
+// fn find_latest_directory(path: &Path) -> std::io::Result<Option<PathBuf>> {
+//     let mut latest: Option<(PathBuf, std::time::SystemTime)> = None;
+//
+//     for entry in std::fs::read_dir(path)? {
+//         let entry = entry?;
+//         let path = entry.path();
+//         if path.is_dir() {
+//             if let Ok(metadata) = entry.metadata() {
+//                 if let Ok(modified) = metadata.modified() {
+//                     if latest.is_none() || modified > latest.as_ref().unwrap().1 {
+//                         latest = Some((path, modified));
+//                     }
+//                 }
+//             }
+//         }
+//     }
+//
+//     Ok(latest.map(|l| l.0))
+// }
+//
+// fn list_directories(path: &Path) -> std::io::Result<Vec<PathBuf>> {
+//     let mut directories = Vec::new();
+//
+//     for entry in std::fs::read_dir(path)? {
+//         let entry = entry?;
+//         if entry.path().is_dir() {
+//             directories.push(entry.path());
+//         }
+//     }
+//
+//     Ok(directories)
+// }
+//
+// // 获取某个交易所的所有币对(获取最新能获取到的)
+// pub fn get_symbols_by_exchange(exchange: &str) -> Value {
+//     let mut symbols = vec![];
+//
+//     let path_str = format!("./db/{}", exchange);
+//     let path = Path::new(&path_str);
+//
+//     if let Some(latest_dir) = find_latest_directory(path).unwrap() {
+//         info!("找到最后一日生成的目录: {}", latest_dir.to_str().unwrap());
+//         let subdirectories = list_directories(&latest_dir).unwrap();
+//         for sub_dir in subdirectories {
+//             symbols.push(sub_dir.file_name().unwrap().to_str().unwrap().to_string())
+//         }
+//     }
+//
+//     info!(?symbols);
+//     return serde_json::to_value(&symbols).unwrap()
+// }
+
+#[tokio::test]
+async fn read_symbols_test() {
+    use global::log_utils::init_log_with_info;
+    init_log_with_info();
+
+    get_symbols_by_exchange("bitget_usdt_swap");
+}
+
+#[tokio::test]
+async fn read_test() {
+    use global::log_utils::init_log_with_info;
+    init_log_with_info();
+
+    let rst = collect_special_trades_json(1712894400000, 1712912400000, "gate_usdt_swap", "CFX_USDT").await;
+    info!("{}", rst)
+}
+
+#[tokio::test]
+async fn write_test() {
+    use std::time::Duration;
+    use tokio::time::sleep;
+
+    // 调用函数,不需要等待它完成
+    write_to_file("{\"key\": \"value\"}".to_string(), "db/test.json".to_string()).await;
+
+    sleep(Duration::from_secs(2)).await;
+}

+ 36 - 0
src/listener_tools.rs

@@ -0,0 +1,36 @@
+use std::collections::HashMap;
+use std::str::FromStr;
+use rust_decimal::prelude::ToPrimitive;
+use tokio::sync::MutexGuard;
+use standard::{SpecialTrade, Trade};
+use crate::json_db_utils::{generate_file_path, minute_to_date, write_to_file};
+
+pub type TradeMap = HashMap<String, Vec<SpecialTrade>>;
+
+// 更新订单流数据
+pub async fn update_trade(new_trade: &Trade, mut trades_map: MutexGuard<'_, TradeMap>, exchange: &str) {
+    if let Some(trades) = trades_map.get_mut(new_trade.symbol.as_str()) {
+        if let Some(last_trade) = trades.last() {
+            // 这里的last_trade.0是以元组形式进行访问。
+            let last_trade_minutes = i64::from_str(last_trade.inner()[1].as_str()).unwrap() / 60000;    // 将毫秒转换成分钟数
+            let new_trade_minutes = new_trade.time.to_i64().unwrap() / 60000;                           // 同上
+
+            // 如果分钟数不同,则清空列表并添加新的trade
+            if last_trade_minutes != new_trade_minutes {
+                let depths_json = serde_json::to_string(trades).unwrap();
+                let date_str = minute_to_date(last_trade_minutes);
+                let path = generate_file_path(exchange, date_str.as_str(), new_trade.symbol.as_str(), "trades", last_trade_minutes);
+
+                // info!(?path);
+
+                write_to_file(depths_json, path).await;
+
+                trades.clear();
+            }
+        }
+        trades.push(SpecialTrade::new(&new_trade));
+    } else {
+        // 如果该symbol不存在,则创建新的Vec并添加trade
+        trades_map.insert(new_trade.symbol.clone(), vec![SpecialTrade::new(&new_trade)]);
+    }
+}

+ 43 - 2
src/main.rs

@@ -1,3 +1,44 @@
-fn main() {
-    println!("Hello, world!");
+mod json_db_utils;
+mod control_c;
+mod server;
+mod listener_tools;
+mod gate_usdt_swap_data_listener;
+mod msv;
+
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::time::Duration;
+use tracing::{info, warn};
+use tracing_appender_timezone::non_blocking::WorkerGuard;
+
+// 日志级别配置
+fn log_level_init(log_str: String, port: u32, app_name: String) -> WorkerGuard {
+    info!("日志级别读取成功:{}。", log_str);
+    global::log_utils::final_init(log_str.as_str(), port, app_name)
+}
+
+#[tokio::main(flavor = "multi_thread")]
+async fn main() {
+    // 日志级别配置
+    let _ = log_level_init("info".to_string(), 8888, "data-center".to_string());
+    // 掌控全局的关闭
+    let running = Arc::new(AtomicBool::new(true));
+    // 初始化数据服务器
+    server::run_server(8888, running.clone());
+    // ctrl c退出检查程序
+    control_c::exit_handler(running.clone());
+    // 启动各交易所的数据监听器
+    gate_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // panic错误捕获,panic级别的错误直接退出
+    // let panic_running = running.clone();
+    std::panic::set_hook(Box::new(move |panic_info| {
+        let msg = format!("type=panic, msg={:?}, location={:?}", panic_info.to_string(), panic_info.location());
+        warn!("{}", msg);
+        // panic_running.store(false, Ordering::Relaxed);
+    }));
+
+    // 每一秒检查一次程序是否结束
+    while running.load(Ordering::Relaxed) {
+        tokio::time::sleep(Duration::from_secs(1)).await;
+    }
 }
 }

+ 368 - 0
src/msv.rs

@@ -0,0 +1,368 @@
+use std::cmp::{max, min};
+use std::str::FromStr;
+use rust_decimal::{Decimal, MathematicalOps};
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use rust_decimal_macros::dec;
+use serde::{Deserialize, Serialize};
+use serde_json::{Value};
+use standard::{SimpleDepth, Trade};
+
+
+/// 技术指标结构体
+/// - `msv(Vec<Vec<Decimal>>)`: msv
+/// - `liqs(Vec<Vec<Decimal>>)`: liqs
+/// - `eprs(Vec<Vec<Decimal>>)`: eprs
+/// - `sigmas(Vec<Vec<Decimal>>)`: sigmas
+/// - `sigma_mas(Vec<Vec<Decimal>>)`: sigma_mas
+/// - `total_size(i64)`: total_size
+/// - `result_size(i64)`: result_size
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Indicators {
+    pub msv: Vec<Vec<Decimal>>,
+    pub liqs: Vec<Vec<Decimal>>,
+    pub eprs: Vec<Vec<Decimal>>,
+    pub sigmas: Vec<Vec<Decimal>>,
+    pub sigma_mas: Vec<Vec<Decimal>>,
+    pub total_size: i64,
+    pub result_size: i64,
+}
+
+// 将trades转换为具体指标 trades 50 [] stime etime
+pub fn generate_msv_by_trades(mut trades: Vec<Trade>, mills_back: Decimal, simple_depths: Vec<SimpleDepth>, start_time: i64, end_time: i64) -> Indicators {
+    // 具体波动
+    let mut msv_data: Vec<Vec<Decimal>> = vec![];
+    // 预期利润幅度(except_profit_rate)
+    let mut epr_data: Vec<Vec<Decimal>> = vec![];
+    // 波动率sigma
+    let mut sigma_data: Vec<Vec<Decimal>> = vec![];
+
+    const GAMMA: Decimal = dec!(0.5);
+
+    // ================== 计算每个点的具体波动率以及回溯幅度 ===================
+    trades.sort_by(|a, b| Decimal::from_str(a.id.as_str()).unwrap().cmp(&Decimal::from_str(b.id.as_str()).unwrap()));
+    for (index, trade) in trades.iter().enumerate() {
+        if index == 0 {
+            continue;
+        }
+
+        // 该元素向前遍历range毫秒
+        let mut range_index = index;
+        // 该区间的预定价格
+        let mut ref_price = trade.price;
+        let mut dissociation = Decimal::ZERO;
+        loop {
+            // 下标合法性判断
+            if range_index == 0 {
+                break;
+            }
+
+            let flag_trade = trades.get(range_index).unwrap();
+            let range_time = trade.time - flag_trade.time;
+            // 判断该ticker是否是range ms以外
+            if range_time > mills_back {
+                break;
+            }
+
+            ref_price = ref_price * GAMMA + flag_trade.price * (Decimal::ONE - GAMMA);
+            dissociation = dissociation + flag_trade.size.abs();
+
+            range_index -= 1;
+        }
+
+        // 获取到range毫秒以后的预定价格,计算回去的幅度
+        let mut future_ref_price = ref_price;
+        let mut future_range_index = index;
+        loop {
+            // 下标合法性判断
+            if future_range_index >= trades.len() {
+                break;
+            }
+
+            let flag_trade = trades.get(future_range_index).unwrap();
+            let range_time = flag_trade.time - trade.time;
+
+            // 判断该ticker是否是range ms以外
+            if range_time > mills_back {
+                break;
+            }
+            future_ref_price = future_ref_price * GAMMA + flag_trade.price * (Decimal::ONE - GAMMA);
+
+            future_range_index += 1;
+        }
+
+        // 计算过去至多100条数据的sigma值 sigma^2 = (1 / (tn-t0))*sum((S(tk) - S(tk-1)) ^ 2)
+        let mut sigma_index = index - 1;
+        let t_last = trade.time;
+
+        let mut _t_first = trade.time;
+        // 右值
+        let mut total_right = Decimal::ZERO;
+        loop {
+            let flag_trade = trades.get(sigma_index).unwrap();
+            let next_trade = trades.get(sigma_index + 1).unwrap();
+
+            // 下标合法性判断
+            if sigma_index == 0 || sigma_index + 100 <= index {
+                _t_first = flag_trade.time;
+                break;
+            }
+
+            // 计算差值
+            let diff = Decimal::ONE - flag_trade.price / next_trade.price;
+            total_right += diff * diff;
+
+            sigma_index = sigma_index - 1;
+        }
+        let sigma_square = if _t_first == t_last {
+            let time_diff = Decimal::ONE;
+            (Decimal::ONE / time_diff) * total_right
+        } else {
+            let time_diff = (t_last - _t_first) / Decimal::ONE_THOUSAND;
+            (Decimal::ONE / time_diff) * total_right
+        };
+        let mut sigma = sigma_square.sqrt().unwrap();
+        sigma.rescale(6);
+        // 计算过去至多100个sigma值的平均值
+        let sigma_ma = if sigma_data.len() > 0 {
+            let mut sigma_ma_index = sigma_data.len();
+            let mut sigma_total = Decimal::ZERO;
+            let mut sigma_count = Decimal::ZERO;
+            loop {
+                if sigma_ma_index == 0 || sigma_ma_index + 99 < sigma_data.len() {
+                    break;
+                }
+                // 步进
+                sigma_ma_index -= 1;
+                // 计算
+                sigma_total += sigma_data[sigma_ma_index][1];
+                sigma_count += Decimal::ONE;
+            }
+            let mut sigma_ma = sigma_total / sigma_count;
+            sigma_ma.rescale(6);
+
+            sigma_ma
+        } else {
+            sigma
+        };
+
+        // ==================== 波动逻辑计算 ====================
+        let last_price = trade.price;
+        let mut rate = Decimal::ONE_HUNDRED * (last_price - ref_price) / ref_price;
+        rate.rescale(2);
+        // 去除小数位之后,可以忽略一些太小的波动,减少图表生成压力
+        if rate.eq(&Decimal::ZERO) {
+            continue;
+        }
+
+        // ==================== 预期利润逻辑计算 ====================
+        // 首先计算未来一段时间的价格与现在的距离
+        let mut future_rate = Decimal::ONE_HUNDRED * (future_ref_price - last_price) / last_price;
+        future_rate.rescale(2);
+        // 根据具体向上波动还是向下波动来计算预期最大利润
+        let epr = if rate > Decimal::ZERO {
+            -future_rate
+        } else {
+            future_rate
+        };
+
+        // 去重,以及保留最大的波动率
+        if msv_data.len() > 0 {
+            let last = msv_data[msv_data.len() - 1].clone();
+            let last_time = last[0];
+            let last_rate = last[1];
+
+            // 如果时间相同,则可能会进行remove等操作
+            if last_time == trade.time {
+                // 如果最新的波动率大于最后波动率
+                if rate.abs() > last_rate.abs() {
+                    msv_data.remove(msv_data.len() - 1);
+                    msv_data.push(vec![trade.time, rate, dissociation]);
+
+                    epr_data.remove(epr_data.len() - 1);
+                    epr_data.push(vec![trade.time, epr]);
+
+                    sigma_data.remove(sigma_data.len() - 1);
+                    sigma_data.push(vec![trade.time, sigma, sigma_ma]);
+                }
+            } else {
+                msv_data.push(vec![trade.time, rate, dissociation]);
+                epr_data.push(vec![trade.time, epr]);
+                sigma_data.push(vec![trade.time, sigma, sigma_ma]);
+            }
+        } else {
+            msv_data.push(vec![trade.time, rate, dissociation]);
+            epr_data.push(vec![trade.time, epr]);
+            sigma_data.push(vec![trade.time, sigma, sigma_ma]);
+        }
+    }
+
+    // 按时间序列填充数据
+    let mut msv_index = 0;
+    let mut final_msv_data: Vec<Vec<Decimal>> = vec![];
+    let mut final_epr_data: Vec<Vec<Decimal>> = vec![];
+    let mut final_sigma_data: Vec<Vec<Decimal>> = vec![];
+    let mut final_sigma_ma_data: Vec<Vec<Decimal>> = vec![];
+
+    let mut depth_index = 0;
+    let mut final_volume_data: Vec<Vec<Decimal>> = vec![];
+
+    let mut index_timestamp = Decimal::from_i64(start_time).unwrap();
+    let last_timestamp = Decimal::from_i64(end_time).unwrap();
+    let step_timestamp = dec!(1000);
+    loop {
+        let mut max_msv_data = Decimal::ZERO;
+        let mut max_msv_qty_data = Decimal::ZERO;
+        let mut max_epr_data = Decimal::ZERO;
+        let mut max_sigma_data = Decimal::ZERO;
+        let mut max_sigma_ma_data = Decimal::ZERO;
+
+        // ====================================== 数据生产 ===============================================
+        // 获取时间范围内的波动率数据
+        loop {
+            // 下标合法性判断
+            if msv_index >= msv_data.len() {
+                break;
+            }
+
+            // msv_data的指定下标数据不在时间范围内(时间范围:指的是[index_timestamp-mills_back, index_timestamp]这个范围)
+            if index_timestamp < msv_data[msv_index][0] {
+                break;
+            }
+
+            // -------------- 大小判断,取值
+            let msv_d = msv_data[msv_index][1];
+            let msv_qty_data = msv_data[msv_index][2];
+            let epr_d = epr_data[msv_index][1];
+            let sigma_d = sigma_data[msv_index][1];
+            let sigma_ma_d = sigma_data[msv_index][2];
+            // msv波动数据
+            if max_msv_data.abs() < msv_d.abs() {
+                max_msv_data = msv_d;
+                max_msv_qty_data = msv_qty_data;
+                max_epr_data = epr_d;
+                max_sigma_data = sigma_d;
+                max_sigma_ma_data = sigma_ma_d;
+            }
+            // // 波动率sigma
+            // if max_sigma_data.abs() < sigma_d {
+            //     max_sigma_data = sigma_d;
+            // }
+
+            // 下标步近
+            msv_index = msv_index + 1;
+        }
+
+        // 获取时间范围内的深度数据、买一及卖一价数据
+        let mut max_size = Decimal::ZERO;
+        let mut min_size = Decimal::ONE_THOUSAND * Decimal::ONE_THOUSAND;
+        loop {
+            // 下标合法性判断
+            if depth_index >= simple_depths.len() {
+                break;
+            }
+            let depth = &simple_depths[depth_index];
+            // 时间范围合法性判断,只统计那一秒以内的深度总交易量
+            if index_timestamp < depth.time {
+                break;
+            }
+            // 这一秒的深度最大值、最小值
+            max_size = max(max_size, depth.size);
+            min_size = min(min_size, depth.size);
+            // 下标步近
+            depth_index += 1;
+        }
+
+        // ====================================== 智能填充数据 ===============================================
+        // 流动性数据叠加
+        // let rst_size = if (max_size == Decimal::ZERO || min_size == Decimal::ONE_THOUSAND * Decimal::ONE_THOUSAND) && final_depth_data.len() > 0 {
+        //     final_depth_data.last().unwrap()[1]
+        // } else {
+        //     if (max_size == Decimal::ZERO || min_size == Decimal::ONE_THOUSAND * Decimal::ONE_THOUSAND) && simple_depths.len() > 0 {
+        //         simple_depths[0].size
+        //     } else {
+        //         if simple_depths.len() > 0 {
+        //             (max_size + min_size) / Decimal::TWO
+        //         } else {
+        //             Decimal::ZERO
+        //         }
+        //     }
+        // };
+        // final_depth_data.push(vec![index_timestamp, rst_size]);
+        //
+        // // 建议开仓距离
+        // let mut rst_spread = if rst_size == Decimal::ZERO {
+        //     Decimal::ZERO
+        // } else {
+        //     dec!(10000) / rst_size
+        // };
+        // rst_spread.rescale(6);
+        // final_spread_data.push(vec![index_timestamp, rst_spread]);
+
+        // 波动率数据处理
+        // 如果这两个值为0,则代表这mills_back毫秒以内是没有数据的,填充0数据,使得x轴是完整的
+        if max_msv_data == Decimal::ZERO {
+            final_msv_data.push(vec![index_timestamp, Decimal::ZERO, Decimal::ZERO]);
+            final_epr_data.push(vec![index_timestamp, Decimal::ZERO]);
+            final_volume_data.push(vec![index_timestamp, Decimal::ZERO]);
+
+            if final_sigma_data.len() > 0 {
+                final_sigma_data.push(vec![index_timestamp, final_sigma_data.last().unwrap()[1]]);
+                final_sigma_ma_data.push(vec![index_timestamp, final_sigma_ma_data.last().unwrap()[1]]);
+            } else {
+                final_sigma_data.push(vec![index_timestamp, Decimal::ZERO]);
+                final_sigma_ma_data.push(vec![index_timestamp, Decimal::ZERO]);
+            }
+
+            // 说明在这个时间范围内是有数据存在的,将各类副图放置完全
+        } else {
+            final_msv_data.push(vec![index_timestamp, max_msv_data, max_msv_qty_data]);
+            final_epr_data.push(vec![index_timestamp, max_epr_data]);
+
+            let mut final_qty = max_msv_qty_data / Decimal::ONE_THOUSAND;
+            final_qty.rescale(2);
+            final_volume_data.push(vec![index_timestamp, final_qty]);
+
+            final_sigma_data.push(vec![index_timestamp, max_sigma_data]);
+            final_sigma_ma_data.push(vec![index_timestamp, max_sigma_ma_data]);
+        }
+
+        // ====================================== 时间步进处理 ======================================
+        // 对时间进行步近
+        index_timestamp = index_timestamp + step_timestamp;
+        // 时间越界
+        if index_timestamp > last_timestamp {
+            break;
+        }
+    }
+
+    // 结果统计
+    let total_size = trades.len().to_i64().unwrap();
+    let result_size = final_msv_data.len().to_i64().unwrap();
+    Indicators {
+        msv: final_msv_data,
+        liqs: final_volume_data,
+        eprs: final_epr_data,
+        sigmas: final_sigma_data,
+        sigma_mas: final_sigma_ma_data,
+        total_size,
+        result_size,
+    }
+}
+
+// 将json转换为trades
+pub fn parse_json_to_trades(trades_json: Value) -> Vec<Trade> {
+    let mut rst = vec![];
+
+    for trade_json in trades_json.as_array().unwrap() {
+        let arr = trade_json.as_array().unwrap();
+        rst.push(Trade {
+            id: arr[0].as_str().unwrap().to_string(),
+            time: Decimal::from_str(arr[1].as_str().unwrap()).unwrap(),
+            size: Decimal::from_str(arr[2].as_str().unwrap()).unwrap(),
+            price: Decimal::from_str(arr[3].as_str().unwrap()).unwrap(),
+            symbol: "".to_string(),
+        });
+    }
+
+    rst
+}

+ 140 - 0
src/server.rs

@@ -0,0 +1,140 @@
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use actix_web::{web, App, HttpResponse, HttpServer, Responder, get};
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+use tracing::{info};
+use crate::json_db_utils::{collect_special_trades_json};
+
+// 定义用于反序列化查询参数的结构体
+#[derive(Serialize, Deserialize, Clone)]
+pub struct SimpleQuery {
+    symbol: Option<String>,
+    exchange: Option<String>,
+    start_time: Option<i64>,
+    end_time: Option<i64>,
+}
+
+impl SimpleQuery {
+    pub fn validate(&self) -> bool {
+        if self.symbol.is_none() {
+            return false
+        }
+
+        if self.exchange.is_none() {
+            return false
+        }
+
+        if self.start_time.is_none()  {
+            return false
+        }
+
+        if self.end_time.is_none()  {
+            return false
+        }
+
+        true
+    }
+}
+
+// 定义用于反序列化查询参数的结构体
+#[derive(Serialize, Deserialize, Clone)]
+pub struct SymbolsQuery {
+    exchange: Option<String>,
+}
+
+// impl SymbolsQuery {
+//     pub fn validate(&self) -> bool {
+//         if self.exchange.is_none() {
+//             return false
+//         }
+//
+//         return true
+//     }
+// }
+#[derive(Serialize, Deserialize, Clone)]
+pub struct ExchangeSpecialQuery {
+    exchange: Option<String>,
+    start_time: Option<i64>,
+    end_time: Option<i64>,
+}
+
+// impl ExchangeSpecialQuery {
+//     pub fn validate(&self) -> bool {
+//         if self.exchange.is_none() {
+//             return false
+//         }
+//
+//         if self.start_time.is_none()  {
+//             return false
+//         }
+//
+//         if self.end_time.is_none()  {
+//             return false
+//         }
+//
+//         true
+//     }
+// }
+
+#[derive(Serialize, Deserialize)]
+pub struct Response {
+    msg: Option<String>,
+    query: Value,
+    data: Value,
+    code: i32,
+}
+
+#[get("/trades")]
+async fn get_trades(query: web::Query<SimpleQuery>) -> impl Responder {
+    if query.validate() {
+        let response_data = collect_special_trades_json(
+            query.start_time.clone().unwrap(),
+            query.end_time.clone().unwrap(),
+            query.exchange.clone().unwrap().as_str(),
+            query.symbol.clone().unwrap().as_str()
+        ).await;
+
+        let response = Response {
+            query: serde_json::to_value(&query.into_inner()).unwrap(),
+            msg: Some("查询成功".to_string()),
+            code: 200,
+            data: json!(response_data),
+        };
+
+        let json_string = serde_json::to_string(&response).unwrap();
+        HttpResponse::Ok().content_type("application/json").body(json_string)
+    } else {
+        let response = Response {
+            query: serde_json::to_value(&query.into_inner()).unwrap(),
+            msg: Some("查询内容有误,必须包含四个参数:[symbol, exchange, start_time, end_time]".to_string()),
+            code: 500,
+            data: Value::Null,
+        };
+
+        let json_string = serde_json::to_string(&response).unwrap();
+        HttpResponse::Ok().content_type("application/json").body(json_string)
+    }
+}
+
+pub fn run_server(port: u32, running: Arc<AtomicBool>) {
+    let addr = format!("0.0.0.0:{}", port);
+    info!("数据服务绑定地址:{}", addr);
+
+    // 启动server
+    let server_fut = HttpServer::new(move || {
+        App::new()
+            .service(get_trades)
+    })
+    .bind(addr)
+    .expect("Bind port error")
+    .run();
+
+    info!("数据仓库服务已运行。");
+
+    let r = running.clone();
+    tokio::spawn(async move {
+        server_fut.await.expect("error running the server");
+        r.store(false, Ordering::Relaxed);
+    });
+}

+ 5 - 0
standard/.gitignore

@@ -0,0 +1,5 @@
+/target
+/.idea
+/logs*
+
+/Cargo.lock

+ 22 - 0
standard/Cargo.toml

@@ -0,0 +1,22 @@
+[package]
+name = "standard"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+global = { path = "../global" }
+exchanges = { path = "../exchanges" }
+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_macros = "1.32.0"
+chrono = "0.4.30"
+futures = "0.3"
+tracing = "0.1"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+toml = "0.5.11"
+futures-channel = "0.3.29"

+ 69 - 0
standard/src/exchange.rs

@@ -0,0 +1,69 @@
+use std::collections::{BTreeMap};
+use std::io::Error;
+use tokio::sync::mpsc::Sender;
+use crate::{Order, Platform};
+use crate::gate_swap::GateSwap;
+
+
+/// 交易所交易模式枚举
+/// - `BinanceSwap`: Binance交易所期货;
+/// - `BinanceSpot`: Binance交易所现货;
+/// - `GateSwap`: Gate交易所期货;
+/// - `GateSpot`: Gate交易所现货;
+/// - `KucoinSwap`: kucoin交易所期货;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ExchangeEnum {
+    GateSwap,
+}
+
+/// Exchange结构体
+///
+/// 方法:
+/// - 创建Exchange:
+///
+/// new(platform: [ExchangeEnum], symbol: `String`, is_colo: `bool`, params: `BTreeMap<String, String>`, order_sender: `Sender<Order>`, error_sender: `Sender<Error>`) -> `Box<dyn Platform + Send + Sync>`
+/// - `platform(`[ExchangeEnum]`)`: 交易所平台枚举
+/// - `symbol(String)`: 币对
+/// - `is_colo(bool)`: 是否开始告诉模式
+/// - `params(BTreeMap<String, String>)`: 登录所需参数
+/// - `order_sender(Sender<Order>)`: 订单消息发送者
+/// - `error_sender(Sender<Error>)`: 错误消息发送者
+///
+/// 示例参数值:
+///
+/// | 交易所枚举 | params参数示例 BTreeMap<String, String> |
+/// | --- | --- |
+/// | BinanceSwap | {"access_key":"your_access_key","secret_key":"your_secret_key"} |
+/// | BinanceSpot | {"access_key":"your_access_key","secret_key":"your_secret_key"} |
+/// | GateSwap | {"access_key":"your_access_key","secret_key":"your_secret_key"} |
+/// | GateSpot | {"access_key":"your_access_key","secret_key":"your_secret_key"} |
+/// | KucoinSwap | {"access_key":"your_access_key","secret_key":"your_secret_key","pass_key":"your_pass_key"} |
+///  ```rust
+/// use std::collections::BTreeMap;
+/// use std::io::Error;
+/// use tokio::sync::mpsc;
+/// use standard::exchange::{Exchange, ExchangeEnum};
+/// use standard::Order;
+///
+/// let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// params.insert("access_key".to_string(), "your_access_key".to_string());
+/// params.insert("secret_key".to_string(), "your_secret_key".to_string());
+/// let (order_sender, _order_receiver): (mpsc::Sender<Order>, mpsc::Receiver<Order>) = mpsc::channel(1024);
+/// let (error_sender, _error_receiver): (mpsc::Sender<Error>, mpsc::Receiver<Error>) = mpsc::channel(1024);
+///
+/// let exchange = Exchange::new(ExchangeEnum::GateSwap, "BTC_USDT".to_string(), false, params, order_sender, error_sender);
+#[derive(Debug, Clone)]
+pub struct Exchange;
+
+impl Exchange {
+    pub async fn new(exchange: ExchangeEnum, symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> Box<dyn Platform + Send + Sync> {
+        match exchange {
+            ExchangeEnum::GateSwap => {
+                Box::new(GateSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+        }
+    }
+}
+
+
+

+ 19 - 0
standard/src/exchange_struct_handler.rs

@@ -0,0 +1,19 @@
+use exchanges::response_base::ResponseData;
+use crate::exchange::ExchangeEnum;
+use crate::{gate_swap_handle};
+use crate::{ Trade };
+
+#[allow(dead_code)]
+pub struct ExchangeStructHandler;
+
+#[allow(dead_code)]
+impl ExchangeStructHandler {
+    // 处理成交信息
+    pub fn trades_handle(exchange: ExchangeEnum, res_data: &ResponseData) -> Vec<Trade> {
+        match exchange {
+            ExchangeEnum::GateSwap => {
+                gate_swap_handle::format_trade_items(&res_data)
+            }
+        }
+    }
+}

+ 645 - 0
standard/src/gate_swap.rs

@@ -0,0 +1,645 @@
+use std::collections::{BTreeMap};
+use std::io::{Error, ErrorKind};
+use std::str::FromStr;
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use serde_json::{json, Value};
+use tracing::{error, info};
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, PositionModeEnum};
+use exchanges::gate_swap_rest::GateSwapRest;
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct GateSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: GateSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl GateSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> GateSwap {
+        let market = Market::new();
+        let mut gate_swap = GateSwap {
+            exchange: ExchangeEnum::GateSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: GateSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+
+        // 修改持仓模式
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mode_result = gate_swap.set_dual_mode(symbol_array[1], true).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("Gate:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("Gate:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 获取市场信息
+        gate_swap.market = GateSwap::get_market(&mut gate_swap).await.unwrap_or(gate_swap.market);
+        return gate_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for GateSwap {
+    // 克隆方法
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+    // 获取交易所模式
+    fn get_self_exchange(&self) -> ExchangeEnum {
+        ExchangeEnum::GateSwap
+    }
+    // 获取交易对
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+    // 获取是否使用高速通道
+    fn get_self_is_colo(&self) -> bool {
+        self.is_colo
+    }
+    // 获取params信息
+    fn get_self_params(&self) -> BTreeMap<String, String> {
+        self.params.clone()
+    }
+    // 获取market信息
+    fn get_self_market(&self) -> Market { self.market.clone() }
+    // 获取请求时间
+    fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+    // 获取请求平均时间
+    fn get_request_avg_delay(&self) -> Decimal { self.request.get_avg_delay() }
+    // 获取请求最大时间
+    fn get_request_max_delay(&self) -> i64 { self.request.get_max_delay() }
+
+    // 获取服务器时间
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let res_data = self.request.get_server_time().await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = res_data_json["server_time"].to_string();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 获取账号信息
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.get_account(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let balance = Decimal::from_str(res_data_json["total"].as_str().unwrap()).unwrap();
+            let available_balance = Decimal::from_str(res_data_json["available"].as_str().unwrap()).unwrap();
+            let frozen_balance = balance - available_balance;
+            let result = Account {
+                coin: symbol_array[1].to_string(),
+                balance,
+                available_balance,
+                frozen_balance,
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "gate_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 获取持仓信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_position(symbol_array[1].to_string().to_lowercase(), self.symbol.clone()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 获取所有持仓
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.get_user_position(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, Decimal::ONE) }).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 获取市场行情
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.get_ticker(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == self.symbol);
+            match ticker_info {
+                None => {
+                    error!("gate_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: chrono::Utc::now().timestamp_millis(),
+                        high: Decimal::from_str(value["high_24h"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low_24h"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["lowest_ask"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["highest_bid"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["volume_24h"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        let symbol_upper = symbol.to_uppercase();
+        let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+        let res_data = self.request.get_ticker(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == symbol_upper);
+            match ticker_info {
+                None => {
+                    error!("gate_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: chrono::Utc::now().timestamp_millis(),
+                        high: Decimal::from_str(value["high_24h"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low_24h"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["lowest_ask"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["highest_bid"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["volume_24h"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.get_market_details(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let market_info = res_data_json.iter().find(|item| item["name"].as_str().unwrap() == self.symbol);
+            match market_info {
+                None => {
+                    error!("gate_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let name = value["name"].as_str().unwrap();
+                    let name_array: Vec<&str> = name.split("_").collect();
+                    let tick_size = Decimal::from_str(value["order_price_round"].as_str().unwrap()).unwrap();
+                    let min_qty = Decimal::from_str(&value["order_size_min"].to_string()).unwrap();
+                    let max_qty = Decimal::from_str(&value["order_size_max"].to_string()).unwrap();
+                    let ct_val = Decimal::from_str(value["quanto_multiplier"].as_str().unwrap()).unwrap();
+
+                    let amount_size = min_qty * ct_val;
+                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+                    let max_notional = max_qty * ct_val;
+
+                    let result = Market {
+                        symbol: name.to_string(),
+                        base_asset: name_array[0].to_string(),
+                        quote_asset: name_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_upper = symbol.to_uppercase();
+        let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+        let res_data = self.request.get_market_details(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let market_info = res_data_json.iter().find(|item| item["name"].as_str().unwrap() == symbol_upper);
+            match market_info {
+                None => {
+                    error!("gate_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let name = value["name"].as_str().unwrap();
+                    let name_array: Vec<&str> = name.split("_").collect();
+                    let tick_size = Decimal::from_str(value["order_price_round"].as_str().unwrap()).unwrap();
+                    let min_qty = Decimal::from_str(&value["order_size_min"].to_string()).unwrap();
+                    let max_qty = Decimal::from_str(&value["order_size_max"].to_string()).unwrap();
+                    let ct_val = Decimal::from_str(value["quanto_multiplier"].as_str().unwrap()).unwrap();
+
+                    let amount_size = min_qty * ct_val;
+                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+                    let max_notional = max_qty * ct_val;
+
+                    let result = Market {
+                        symbol: name.to_string(),
+                        base_asset: name_array[0].to_string(),
+                        quote_asset: name_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    // 获取订单详情
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let id = if order_id.eq("") { format!("t-{}", custom_id) } else { order_id.to_string() };
+        let res_data = self.request.get_order_details(symbol_array[1].to_string().to_lowercase(), id).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = format_order_item(res_data_json, ct_val);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 获取订单列表
+    async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_orders(symbol_array[1].to_string().to_lowercase(), status.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let order_info: Vec<_> = res_data_json.iter().filter(|item| item["contract"].as_str().unwrap_or("") == self.symbol).collect();
+            let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val)).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 下单接口
+    async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let mut params = json!({
+            "text": format!("t-{}", custom_id),
+            "contract": self.symbol.to_string(),
+            "price": price.to_string(),
+        });
+        let size = (amount / ct_val).floor();
+        if price.eq(&Decimal::ZERO) {
+            params["tif"] = json!("ioc".to_string());
+        }
+        match origin_side {
+            "kd" => {
+                params["reduce_only"] = json!(false);
+                params["size"] = json!(size.to_i64());
+            }
+            "pd" => {
+                params["reduce_only"] = json!(true);
+                params["size"] = serde_json::Value::from((-size).to_i64());
+            }
+            "kk" => {
+                params["reduce_only"] = json!(false);
+                params["size"] = serde_json::Value::from((-size).to_i64());
+            }
+            "pk" => {
+                params["reduce_only"] = json!(true);
+                params["size"] = json!(size.to_i64());
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(symbol_array[1].to_string().to_lowercase(), params).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = format_order_item(res_data_json, ct_val);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    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_upper = symbol.to_uppercase();
+        let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+        let mut params = json!({
+            "text": format!("t-{}", custom_id),
+            "contract": symbol_upper.to_string(),
+            "price": price.to_string(),
+        });
+        let size = (amount / ct_val).floor();
+        if price.eq(&Decimal::ZERO) {
+            params["tif"] = json!("ioc".to_string());
+        }
+        match origin_side {
+            "kd" => {
+                params["reduce_only"] = json!(false);
+                params["size"] = json!(size.to_i64());
+            }
+            "pd" => {
+                params["reduce_only"] = json!(true);
+                params["size"] = serde_json::Value::from((-size).to_i64());
+            }
+            "kk" => {
+                params["reduce_only"] = json!(false);
+                params["size"] = serde_json::Value::from((-size).to_i64());
+            }
+            "pk" => {
+                params["reduce_only"] = json!(true);
+                params["size"] = json!(size.to_i64());
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(symbol_array[1].to_string().to_lowercase(), params).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = format_order_item(res_data_json, ct_val);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    // 撤销订单
+    async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let settle = symbol_array[1].to_string().to_lowercase();
+        let id = if order_id.eq("") { format!("t-{}", custom_id) } else { order_id.to_string() };
+        let res_data = self.request.cancel_order(settle, id.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = format_order_item(res_data_json, ct_val);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 批量撤销订单
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.cancel_orders(symbol_array[1].to_string().to_lowercase(), self.symbol.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let result = res_data_json.iter().map(|item| format_order_item(item.clone(), ct_val)).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let orders_res_data = self.request.get_orders(symbol_array[1].to_string().to_lowercase(), "open".to_string()).await;
+        if orders_res_data.code == 200 {
+            let mut result = vec![];
+            let orders_res_data_json = orders_res_data.data.as_array().unwrap();
+            for order in orders_res_data_json {
+                let cancel_res_data = self.request.cancel_orders(symbol_array[1].to_string().to_lowercase(), order["contract"].as_str().unwrap().to_string()).await;
+                if cancel_res_data.code == 200 {
+                    let cancel_res_data_json = cancel_res_data.data.as_array().unwrap();
+                    for cancel in cancel_res_data_json {
+                        result.push(format_order_item(cancel.clone(), ct_val))
+                    };
+                } else {
+                    return Err(Error::new(ErrorKind::Other, cancel_res_data.to_string()));
+                }
+            }
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, orders_res_data.to_string()))
+        }
+    }
+
+    async fn take_stop_loss_order(&mut self, stop_price: Decimal, price: Decimal, side: &str) -> Result<Value, Error>
+    {
+        let mut params = json!({});
+        let mut initial = json!({
+            "contract": "XRP_USDT",
+
+            "price": price.to_string(),
+
+            "tif": "ioc",
+
+            // 是否只减仓
+            "reduce_only": true,
+
+            // [平多:close_long, 平空:close_short]
+            // "auto_size": "close_long"
+        });
+        let mut trigger = json!({
+            // [平多:close-long-position, 平空:close-short-position]
+            // "order_type": "close-long-position",
+
+            // 一般都默认用0
+            "strategy_type": 0,
+
+            // [0 - 最新成交价,1 - 标记价格,2 - 指数价格]
+            "price_type": 0,
+
+            // [1: 引用价格大于等于我们传的价格,2:引用价格小于等于我们传的价格]
+            // 在止损的情况下:
+            //     1 可以理解为向上突破触发价(一般是给空单用)
+            //     2 可以理解为向下突破触发价(一般是给多单用)
+            // "rule": 2,
+
+            // 订单触发价格
+            "price": stop_price.to_string(),
+        });
+
+        match side {
+            "kd" => {
+                initial["auto_size"] = json!("close_long");
+                trigger["order_type"] = json!("close-long-position");
+                trigger["rule"] = json!(2);
+            },
+            "kk" => {
+                initial["auto_size"] = json!("close_short");
+                trigger["order_type"] = json!("close-short-position");
+                trigger["rule"] = json!(1);
+            },
+            _ => {
+                error!("gate swap 止损单side错误: {}", side);
+            }
+        }
+
+        params["initial"] = initial;
+        params["trigger"] = trigger;
+
+        let binding = self.symbol.clone().to_lowercase();
+        let symbol_split: Vec<&str> = binding.split("_").collect();
+        let base_coin = symbol_split[1].to_string();
+        let response_data = self.request.place_price_order(base_coin, params).await;
+        if response_data.code == 200 {
+            Ok(response_data.data)
+        } else {
+            Err(Error::new(ErrorKind::Other, response_data.to_string()))
+        }
+    }
+
+    async fn cancel_stop_loss_order(&mut self, order_id: &str) -> Result<Value, Error> {
+        let binding = self.symbol.clone().to_lowercase();
+        let symbol_split: Vec<&str> = binding.split("_").collect();
+        let base_coin = symbol_split[1].to_string();
+
+        let response_data = self.request.cancel_price_order(base_coin, order_id.to_string()).await;
+        if response_data.code == 200 {
+            Ok(response_data.data)
+        } else {
+            Err(Error::new(ErrorKind::Other, response_data.to_string()))
+        }
+    }
+
+    // 设置持仓模式
+    async fn set_dual_mode(&mut self, coin: &str, is_dual_mode: bool) -> Result<String, Error> {
+        let coin_format = coin.to_string().to_lowercase();
+        let res_data = self.request.setting_dual_mode(coin_format, is_dual_mode).await;
+        if res_data.code == 200 {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result.to_string())
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    // 更新双持仓模式下杠杆
+    async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.setting_dual_leverage(symbol_array[1].to_string().to_lowercase(), self.symbol.to_string(), leverage.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result.to_string())
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate:该交易所方法未实现".to_string())) }
+
+    // 交易账户互转
+    async fn wallet_transfers(&mut self, coin: &str, from: &str, to: &str, amount: Decimal) -> Result<String, Error> {
+        let coin_format = coin.to_string().to_lowercase();
+        let res_data = self.request.wallet_transfers(coin_format.clone(), from.to_string(), to.to_string(), amount.to_string(), coin_format.clone()).await;
+        if res_data.code == 200 {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result.to_string())
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+}
+
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
+    let mut position_mode = match position["mode"].as_str().unwrap_or("") {
+        "single" => PositionModeEnum::Both,
+        "dual_long" => PositionModeEnum::Long,
+        "dual_short" => PositionModeEnum::Short,
+        _ => {
+            error!("gate_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("gate_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size = Decimal::from_str(&position["size"].to_string()).unwrap();
+    let amount = size * ct_val;
+    match position_mode {
+        PositionModeEnum::Both => {
+            position_mode = match amount {
+                amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+                amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+                _ => { PositionModeEnum::Both }
+            }
+        }
+        _ => {}
+    }
+    Position {
+        symbol: position["contract"].as_str().unwrap_or("").parse().unwrap(),
+        margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(position["entry_price"].as_str().unwrap()).unwrap(),
+        profit: Decimal::from_str(position["unrealised_pnl"].as_str().unwrap()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(position["margin"].as_str().unwrap()).unwrap(),
+    }
+}
+
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+    let status = order["status"].as_str().unwrap_or("");
+    let text = order["text"].as_str().unwrap_or("");
+    let size = Decimal::from_str(&order["size"].to_string()).unwrap();
+    let left = Decimal::from_str(&order["left"].to_string()).unwrap();
+
+    let amount = size * ct_val;
+    let deal_amount = (size - left) * ct_val;
+    let custom_status = if status == "finished" { "REMOVE".to_string() } else if status == "open" { "NEW".to_string() } else {
+        error!("gate_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order);
+        panic!("gate_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order)
+    };
+    let rst_order = Order {
+        id: order["id"].to_string(),
+        custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
+        price: Decimal::from_str(order["price"].as_str().unwrap()).unwrap(),
+        amount,
+        deal_amount,
+        avg_price: Decimal::from_str(&order["fill_price"].as_str().unwrap()).unwrap(),
+        status: custom_status,
+        order_type: "limit".to_string()
+    };
+    return rst_order;
+}

+ 22 - 0
standard/src/gate_swap_handle.rs

@@ -0,0 +1,22 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use exchanges::response_base::ResponseData;
+use crate::{Trade};
+
+pub fn format_trade_items(res_data: &ResponseData) -> Vec<Trade> {
+    let result = res_data.data.as_array().unwrap();
+    let mut trades = vec![];
+
+    for item in result {
+        trades.push(Trade {
+            id: item["id"].to_string(),
+            time: Decimal::from_i64(item["create_time_ms"].as_i64().unwrap()).unwrap(),
+            size: Decimal::from_i64(item["size"].as_i64().unwrap()).unwrap(),
+            price: Decimal::from_str(item["price"].as_str().unwrap().to_string().as_str()).unwrap(),
+            symbol: item["contract"].as_str().unwrap().to_string(),
+        })
+    }
+
+    return trades;
+}

+ 586 - 0
standard/src/lib.rs

@@ -0,0 +1,586 @@
+use std::collections::{BTreeMap};
+use std::io::{Error};
+use async_trait::async_trait;
+use rust_decimal::Decimal;
+use serde_json::Value;
+use serde::{Serialize, Deserialize};
+
+use crate::exchange::ExchangeEnum;
+
+// 引入工具模块
+pub mod utils;
+// 引入exchange模块
+pub mod exchange;
+pub mod exchange_struct_handler;
+// 引入gate模块
+mod gate_swap;
+pub mod gate_swap_handle;
+
+/// 持仓模式枚举
+/// - `Both`:单持仓方向
+/// - `LONG`:多仓
+/// - `SHORT`:空仓
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PositionModeEnum {
+    Both,
+    Long,
+    Short,
+}
+
+/// Account结构体(账户信息)
+/// - `coin(String)`: 货币;
+/// - `balance(Decimal)`: 总计计价币数量;
+/// - `available_balance(Decimal)`: 可用计价币数量;
+/// - `frozen_balance(Decimal)`: balance挂单的冻结数量
+/// - `stocks(Decimal)`: 总计交易币数量
+/// - `available_stocks(Decimal)`: 可用交易币数量
+/// - `frozen_stocks(Decimal)`: stocks挂单的冻结数量
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Account {
+    pub coin: String,
+    pub balance: Decimal,
+    pub available_balance: Decimal,
+    pub frozen_balance: Decimal,
+    pub stocks: Decimal,
+    pub available_stocks: Decimal,
+    pub frozen_stocks: Decimal,
+}
+
+impl Account {
+    pub fn new() -> Account {
+        Account {
+            coin: "".to_string(),
+            balance: Default::default(),
+            available_balance: Default::default(),
+            frozen_balance: Default::default(),
+            stocks: Default::default(),
+            available_stocks: Default::default(),
+            frozen_stocks: Default::default(),
+        }
+    }
+}
+
+/// 交易结构体(订单流)
+/// - `id(String)`: id
+/// - `time(Decimal)`: 交易更新时间戳(ms)
+/// - `size(Decimal)`: 交易量,负数是卖方
+/// - `price(Decimal)`: 成交价格
+/// - `symbol(String)`: 成交符号
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Trade {
+    pub id: String,
+    pub time: Decimal,
+    pub size: Decimal,
+    pub price: Decimal,
+    pub symbol: String
+}
+
+/// 特殊压缩结构体(订单流)
+/// - `0(Decimal)`: id
+/// - `1(Decimal)`: 交易更新时间戳(ms)
+/// - `2(Decimal)`: 交易量,负数是卖方
+/// - `3(Decimal)`: 成交价格
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct SpecialTrade(pub Vec<String>);
+impl SpecialTrade {
+    // 获取内部 Vec<Decimal> 的引用
+    pub fn inner(&self) -> &Vec<String> {
+        &self.0
+    }
+
+    // 获取内部 Vec<Decimal> 的可变引用
+    pub fn inner_mut(&mut self) -> &mut Vec<String> {
+        &mut self.0
+    }
+
+    // 索引访问特定元素
+    pub fn get(&self, index: usize) -> Option<&String> {
+        self.0.get(index)
+    }
+
+    pub fn new(trade: &Trade) -> SpecialTrade {
+        SpecialTrade(vec![trade.id.clone(), trade.time.to_string(), trade.size.to_string(), trade.price.to_string()])
+    }
+}
+
+/// Depth结构体(市场深度)
+/// - `time(Decimal)`: 深度更新时间戳(ms);
+/// - `symbol(String)`: 币对符号;
+/// - `asks(Vec<MarketOrder>)`: 卖方深度列表;
+/// - `bids(Vec<MarketOrder>)`: 买方深度列表;
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Depth {
+    pub time: Decimal,
+    pub symbol: String,
+    pub asks: Vec<OrderBook>,
+    pub bids: Vec<OrderBook>,
+}
+
+impl Depth {
+    pub fn new() -> Depth {
+        Depth {
+            time: Decimal::ZERO,
+            symbol: String::new(),
+            asks: vec![],
+            bids: vec![],
+        }
+    }
+}
+
+/// 特殊Depth结构体(市场深度),用于存放到本地数据库中
+/// - `a(Vec<Vec<Decimal>>)`: asks;
+/// - `b(Vec<Vec<Decimal>>)`: bids;
+/// - `t(Decimal)`: 数据生成时间
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct SpecialDepth {
+    pub b: Vec<Vec<Decimal>>,
+    pub a: Vec<Vec<Decimal>>,
+    pub t: Decimal,
+}
+
+impl SpecialDepth {
+    pub fn new(depth: &Depth) -> SpecialDepth {
+        let bids = depth.bids.iter().map(|ob| vec![ob.price, ob.amount]).collect::<Vec<_>>();
+        let asks = depth.asks.iter().map(|ob| vec![ob.price, ob.amount]).collect::<Vec<_>>();
+        SpecialDepth {
+            b: bids,
+            a: asks,
+            t: depth.time,
+        }
+    }
+}
+
+// 简易深度信息
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct SimpleDepth {
+    pub time: Decimal,
+    pub size: Decimal,
+    pub b1: Decimal,
+    pub a1: Decimal,
+    pub symbol: String,
+}
+
+impl SimpleDepth {
+    pub fn new(depth: &Depth) -> SimpleDepth {
+        let mut total_size = Decimal::ZERO;
+
+        for ask in &depth.asks {
+            total_size += ask.price * ask.amount;
+        }
+
+        for bid in &depth.bids {
+            total_size += bid.price * bid.amount;
+        }
+
+        total_size.rescale(2);
+
+        SimpleDepth {
+            time: depth.time,
+            size: total_size,
+            b1: depth.bids[0].price,
+            a1: depth.asks[0].price,
+            symbol: depth.symbol.clone(),
+        }
+    }
+}
+
+/// 特殊Ticker结构体(市场行情)
+/// - `sell(Decimal)`: 卖一价
+/// - `buy(Decimal)`: 买一价
+/// - `mid_price(Decimal)`: 平均价
+/// - `t(Decimal)`: 数据更新id
+/// - `create_at(i64)`: 数据生成时间
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
+pub struct SpecialTicker {
+    pub sell: Decimal,
+    pub buy: Decimal,
+    pub mid_price: Decimal,
+    pub t: Decimal,
+    pub create_at: i64
+}
+
+impl SpecialTicker {
+    pub fn new() -> SpecialTicker {
+        SpecialTicker {
+            sell: Default::default(),
+            buy: Default::default(),
+            mid_price: Default::default(),
+            t: Default::default(),
+            create_at: 0,
+        }
+    }
+}
+
+/// OrderBook结构体(市场深度单)
+/// - `price(Decimal)`: 价格
+/// - `amount(Decimal)`: 数量
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct OrderBook {
+    pub price: Decimal,
+    pub amount: Decimal,
+}
+
+impl OrderBook {
+    pub fn new() -> OrderBook {
+        OrderBook {
+            price: Default::default(),
+            amount: Default::default(),
+        }
+    }
+}
+
+/// Record结构体(标准的OHLC结构)
+/// - `time(i64)`: 时间戳;
+/// - `open(Decimal)`: 开盘价;
+/// - `high(Decimal)`: 最高价;
+/// - `low(Decimal):` 最低价;
+/// - `close(Decimal)`: 收盘价;
+/// - `volume(Decimal)`: 交易量;
+/// - `symbol(String)`: 交易对;
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Record {
+    pub time: Decimal,
+    pub open: Decimal,
+    pub high: Decimal,
+    pub low: Decimal,
+    pub close: Decimal,
+    pub volume: Decimal,
+    pub symbol: String
+}
+
+impl Record {
+    pub fn new() -> Record {
+        Record {
+            time: Default::default(),
+            open: Default::default(),
+            high: Default::default(),
+            low: Default::default(),
+            close: Default::default(),
+            volume: Default::default(),
+            symbol: "".to_string(),
+        }
+    }
+}
+
+/// 特殊Order结构体(订单)
+/// - `name<String>`: 平台信息;
+/// - `order<Vec<Order>>`: 订单信息数组;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SpecialOrder {
+    pub name: String,
+    pub order: Vec<Order>,
+}
+
+impl SpecialOrder {
+    pub fn new() -> SpecialOrder {
+        SpecialOrder {
+            name: "".to_string(),
+            order: vec![],
+        }
+    }
+}
+
+/// Order结构体(订单)
+/// - `id(String)`: 交易单唯一标识
+/// - `custom_id(String)`: 自定义Id
+/// - `price(Decimal)`: 下单价格
+/// - `amount(Decimal)`: 下单数量
+/// - `deal_amount(Decimal)`: 成交数量
+/// - `avg_price(Decimal)`: 成交均价
+/// - `status(String)`: 订单状态
+/// - `order_type(String)`: 订单类型
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Order {
+    pub id: String,
+    pub custom_id: String,
+    pub price: Decimal,
+    pub amount: Decimal,
+    pub deal_amount: Decimal,
+    pub avg_price: Decimal,
+    pub status: String,
+    pub order_type: String,
+}
+
+impl Order {
+    pub fn new() -> Order {
+        Order {
+            id: "".to_string(),
+            custom_id: "".to_string(),
+            price: Default::default(),
+            amount: Default::default(),
+            deal_amount: Default::default(),
+            avg_price: Default::default(),
+            status: "".to_string(),
+            order_type: "".to_string()
+        }
+    }
+}
+
+/// Ticker结构体(市场行情)
+/// - `time(i64)`: 毫秒级别时间戳
+/// - `high(Decimal)`: 最高价
+/// - `low(Decimal)`: 最低价
+/// - `sell(Decimal)`: 卖一价
+/// - `buy(Decimal)`: 买一价
+/// - `last(Decimal)`: 最后成交价
+/// - `volume(Decimal)`: 最近成交量
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Ticker {
+    pub time: i64,
+    pub high: Decimal,
+    pub low: Decimal,
+    pub sell: Decimal,
+    pub buy: Decimal,
+    pub last: Decimal,
+    pub volume: Decimal,
+}
+
+impl Ticker {
+    pub fn new() -> Ticker {
+        Ticker {
+            time: 0,
+            high: Default::default(),
+            low: Default::default(),
+            sell: Default::default(),
+            buy: Default::default(),
+            last: Default::default(),
+            volume: Default::default(),
+        }
+    }
+}
+
+/// Market结构体(交易品种的市场信息)
+/// - `symbol(String)`: 交易对
+/// - `base_asset(String)`: 交易币
+/// - `quote_asset(String)`: 计价币
+/// - `tick_size(Decimal)`: 价格最小变动数值
+/// - `amount_size(Decimal)`: 下单量最小变动数值
+/// - `price_precision(Decimal)`: 价格精度
+/// - `amount_precision(Decimal)`: 下单量精度
+/// - `min_qty(Decimal)`: 最小下单量
+/// - `max_qty(Decimal)`: 最大下单量
+/// - `min_notional(Decimal)`: 最小下单金额
+/// - `max_notional(Decimal)`: 最大下单金额
+/// - `ct_val(Decimal)`: 合约价值
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Market {
+    pub symbol: String,
+    pub base_asset: String,
+    pub quote_asset: String,
+    pub tick_size: Decimal,
+    pub amount_size: Decimal,
+    pub price_precision: Decimal,
+    pub amount_precision: Decimal,
+    pub min_qty: Decimal,
+    pub max_qty: Decimal,
+    pub min_notional: Decimal,
+    pub max_notional: Decimal,
+    pub ct_val: Decimal,
+}
+
+impl Market {
+    pub fn new() -> Market {
+        Market {
+            symbol: "".to_string(),
+            base_asset: "".to_string(),
+            quote_asset: "".to_string(),
+            tick_size: Default::default(),
+            amount_size: Default::default(),
+            price_precision: Default::default(),
+            amount_precision: Default::default(),
+            min_qty: Default::default(),
+            max_qty: Default::default(),
+            min_notional: Default::default(),
+            max_notional: Default::default(),
+            ct_val: Default::default(),
+        }
+    }
+}
+
+/// Position结构体(仓位信息)
+/// - `symbol(String)`: 币对
+/// - `margin_level(Decimal)`: 持仓杆杠大小
+/// - `amount(Decimal)`: 持仓量
+/// - `frozen_amount(Decimal)`: 仓位冻结量
+/// - `price(Decimal)`: 持仓均价
+/// - `profit(Decimal)`: 持仓浮动盈亏
+/// - `position_mode(PositionModeEnum)`: 持仓模式
+/// - `margin(Decimal)`: 仓位占用的保证金
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Position {
+    pub symbol: String,
+    pub margin_level: Decimal,
+    pub amount: Decimal,
+    pub frozen_amount: Decimal,
+    pub price: Decimal,
+    pub profit: Decimal,
+    pub position_mode: PositionModeEnum,
+    pub margin: Decimal,
+}
+
+impl Position {
+    pub fn new() -> Position {
+        Position {
+            symbol: "".to_string(),
+            margin_level: Default::default(),
+            amount: Default::default(),
+            frozen_amount: Default::default(),
+            price: Default::default(),
+            profit: Default::default(),
+            position_mode: PositionModeEnum::Both,
+            margin: Default::default(),
+        }
+    }
+}
+
+/// 交易所统一方法接口
+///
+/// 使用方法前需实例化
+/// ```rust
+/// use std::collections::BTreeMap;
+/// use standard::exchange::{Exchange, ExchangeEnum};
+///
+/// let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// params.insert("access_key".to_string(), "your_access_key".to_string());
+/// params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+/// ```
+/// 获取当前交易所交易模式
+/// - fn get_self_exchange(&self) -> ExchangeEnum;
+/// ```rust
+/// # use std::collections::BTreeMap;
+/// # use standard::exchange::{Exchange, ExchangeEnum};
+/// # let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// # params.insert("access_key".to_string(), "your_access_key".to_string());
+/// # params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// # // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+///
+/// // exchange.get_self_exchange();
+/// ```
+/// 获取当前是否使用高速模式
+/// - fn get_self_is_colo(&self) -> bool;
+/// ```rust
+/// # use std::collections::BTreeMap;
+/// # use standard::exchange::{Exchange, ExchangeEnum};
+/// # let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// # params.insert("access_key".to_string(), "your_access_key".to_string());
+/// # params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// # // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+///
+/// // exchange.get_self_is_colo();
+/// ```
+/// 获取当前是否使用登录
+/// - fn get_self_is_login(&self) -> bool;
+/// ```rust
+/// # use std::collections::BTreeMap;
+/// # use standard::exchange::{Exchange, ExchangeEnum};
+/// # let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// # params.insert("access_key".to_string(), "your_access_key".to_string());
+/// # params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// # // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+///
+/// // exchange.get_self_is_login();
+/// ```
+/// 获取登录params信息
+/// - fn get_self_params(&self) -> BTreeMap<String, String>;
+/// ```rust
+/// # use std::collections::BTreeMap;
+/// # use standard::exchange::{Exchange, ExchangeEnum};
+/// # let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// # params.insert("access_key".to_string(), "your_access_key".to_string());
+/// # params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// # // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+///
+/// // exchange.get_self_params();
+/// ```
+/// 获取账号信息
+/// - async fn get_account(&self, symbol: &str) -> Result<Account, Error>;
+/// ```rust
+/// # use std::collections::BTreeMap;
+/// # use standard::exchange::{Exchange, ExchangeEnum};
+/// # let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// # params.insert("access_key".to_string(), "your_access_key".to_string());
+/// # params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// # // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+///
+/// // exchange.get_account()
+/// ```
+/// 订阅账号信息
+/// ```rust
+/// # use std::collections::BTreeMap;
+/// # use standard::exchange::{Exchange, ExchangeEnum};
+/// # let mut params:BTreeMap<String,String> = BTreeMap::new();
+/// # params.insert("access_key".to_string(), "your_access_key".to_string());
+/// # params.insert("access_key".to_string(), "your_secret_key".to_string());
+/// # // let exchange = Exchange::new(ExchangeEnum::BinanceSwap, "BTC_USDT".to_string(), true, params);
+///
+/// // exchange.subscribe_account();
+/// ```
+#[async_trait]
+pub trait Platform {
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync>;
+    // 获取当前交易所交易模式
+    fn get_self_exchange(&self) -> ExchangeEnum;
+    // 获取交易对
+    fn get_self_symbol(&self) -> String;
+    // 获取当前是否使用高速模式
+    fn get_self_is_colo(&self) -> bool;
+    // 获取登录params信息
+    fn get_self_params(&self) -> BTreeMap<String, String>;
+    // 获取market信息
+    fn get_self_market(&self) -> Market;
+    // 获取请求时间
+    fn get_request_delays(&self) -> Vec<i64>;
+    // 获取请求平均时间
+    fn get_request_avg_delay(&self) -> Decimal;
+    // 获取请求最大时间
+    fn get_request_max_delay(&self) -> i64;
+    // 获取服务器时间
+    async fn get_server_time(&mut self) -> Result<String, Error>;
+    // 获取账号信息
+    async fn get_account(&mut self) -> Result<Account, Error>;
+    // 获取现货账号信息
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error>;
+    // 获取持仓信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error>;
+    // 获取所有持仓
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error>;
+    // 获取市场行情
+    async fn get_ticker(&mut self) -> Result<Ticker, Error>;
+    // 获取市场行情自定义交易对
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error>;
+    // 查询所有的市场信息
+    async fn get_market(&mut self) -> Result<Market, Error>;
+    // 查询所有的市场信息自定义交易对
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error>;
+    // 查询订单详情
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error>;
+    // 获取订单列表
+    async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error>;
+    // 下单接口
+    async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error>;
+    // 下单接口自定义交易对
+    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>;
+    // 撤销订单
+    async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error>;
+    // 批量撤销订单
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error>;
+    // 撤销所有订单
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error>;
+    /// 下一个止损单
+    /// - stop_price: 触发价
+    /// - price: 委托价,0就市价委托
+    /// - side: 止损哪个方向[long多单,short空单]
+    async fn take_stop_loss_order(&mut self, stop_price: Decimal, price: Decimal, side: &str) -> Result<Value, Error>;
+    /// 撤销止损订单
+    /// - order_id: 需要撤销的id
+    async fn cancel_stop_loss_order(&mut self, order_id: &str) -> Result<Value, Error>;
+    // 设置持仓模式
+    async fn set_dual_mode(&mut self, coin: &str, is_dual_mode: bool) -> Result<String, Error>;
+    // 更新双持仓模式下杠杆
+    async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error>;
+    // 设置自动追加保证金
+    async fn set_auto_deposit_status(&mut self, status: bool) -> Result<String, Error>;
+    // 交易账户互转
+    async fn wallet_transfers(&mut self, coin: &str, from: &str, to: &str, amount: Decimal) -> Result<String, Error>;
+}

+ 16 - 0
standard/src/utils.rs

@@ -0,0 +1,16 @@
+use tracing::trace;
+use exchanges::proxy;
+
+/// 修改交易对连接符号
+/// - `symbol(str)`: 交易对, "BTC_USDT", 默认以下划线传递
+/// - `pat(str)`: 替换字符, "-", 把 “_” 替换为 "-"
+pub fn format_symbol(symbol: String, pat: &str) -> String {
+    return symbol.to_uppercase().replace("_", pat);
+}
+
+// 检测是否走代理
+pub fn proxy_handle() {
+    if proxy::ParsingDetail::http_enable_proxy() {
+        trace!("检测有代理配置,配置走代理");
+    }
+}

+ 581 - 0
standard/tests/exchange_test.rs

@@ -0,0 +1,581 @@
+// 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};
+// // use exchanges::kucoin_swap_ws::{KucoinSwapLogin, KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
+// // 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::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::{bitget_spot_handle, Order, Platform, utils};
+// use standard::{okx_handle, Order, Platform, utils};
+//
+// // 创建实体
+// #[allow(dead_code)]
+// pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn Platform> {
+//     utils::proxy_handle();
+//     let (order_sender, _order_receiver): (Sender<Order>, Receiver<Order>) = channel(1024);
+//     let (error_sender, _error_receiver): (Sender<Error>, Receiver<Error>) = channel(1024);
+//
+//     let account_info = global::account_info::get_account_info("../test_account.toml");
+//     match exchange {
+//         ExchangeEnum::BinanceSwap => {
+//             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;
+//             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;
+//             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::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
+//         }
+//     }
+// }
+//
+// #[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> {
+//     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_with_tag(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);
+//             // let name = format!("binance_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 = BinanceSwapLogin {
+//             //     api_key: account_info.binance_access_key,
+//             //     api_secret: account_info.binance_secret_key,
+//             // };
+//             // let mut exchange_wss;
+//             // exchange_wss = BinanceSwapWs::new_with_tag(name, false, Option::from(params), BinanceSwapWsType::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_handle::handle_special_depth(data);
+//             //                         trace!(?result)
+//             //                     }
+//             //                 }
+//             //                 "ticker" => {
+//             //                     if data.data != "" {
+//             //                         let result = binance_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::KucoinSwap => {
+//             // let symbol_format = format!("{}M", utils::format_symbol(symbol.to_string(), ""));
+//             // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
+//             //
+//             // let name = format!("kucoin_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 = KucoinSwapLogin {
+//             //     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) {
+//             //     exchange_wss = KucoinSwapWs::new_with_tag(name, false, Option::from(params), KucoinSwapWsType::Public).await;
+//             // } else {
+//             //     exchange_wss = KucoinSwapWs::new_with_tag(name, false, Option::from(params), KucoinSwapWsType::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" => {
+//             //                     let result = kucoin_handle::handle_special_depth(data);
+//             //                     trace!(?result)
+//             //                 }
+//             //                 "ticker" => {
+//             //                     let result = kucoin_handle::handle_special_ticker(data);
+//             //                     trace!(?result)
+//             //                 }
+//             //                 "account" => {
+//             //                     let result = kucoin_handle::handle_account_info(data, symbol_back.clone());
+//             //                     trace!(?result)
+//             //                 }
+//             //                 "position" => {
+//             //                     let result = kucoin_handle::handle_position(data, dec!(1));
+//             //                     trace!(?result)
+//             //                 }
+//             //                 "orders" => {
+//             //                     let result = kucoin_handle::handle_order(data, dec!(0.001));
+//             //                     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_with_tag(name, false, Option::from(params), KucoinSpotWsType::Public).await
+//             // } else {
+//             //     KucoinSpotWs::new_with_tag(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));
+//             //
+//             // let params = GateSwapLogin {
+//             //     api_key: account_info.gate_access_key,
+//             //     secret: account_info.gate_secret_key,
+//             // };
+//             // let mut exchange_wss = GateSwapWs::new_with_tag(name, false, Option::from(params), GateSwapWsType::PublicAndPrivate("usdt".to_string()));
+//             // 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_with_tag(name, false, Option::from(params), BitgetSpotWsType::Public)
+//             // } else {
+//             //     BitgetSpotWs::new_with_tag(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_with_tag(name, false, Option::from(params), OkxSwapWsType::Public)
+//             } else if ["account", "orders", "position"].contains(&mold) {
+//                 OkxSwapWs::new_with_tag(name, false, Option::from(params), OkxSwapWsType::Private)
+//             } else {
+//                 OkxSwapWs::new_with_tag(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();
+//         }
+//         _ => {
+//             error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+//             panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
+//         }
+//     }
+// }

+ 92 - 0
standard/tests/gate_handle_test.rs

@@ -0,0 +1,92 @@
+mod exchange_test;
+
+use std::collections::BTreeMap;
+use tracing::{instrument, trace};
+use exchanges::gate_swap_rest::GateSwapRest;
+use exchanges::gate_swap_ws::GateSwapSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::{test_new_exchange_wss};
+
+const SYMBOL: &str = "BTC_USDT";
+
+async fn get_user_id() -> String {
+    let account_info = global::account_info::get_account_info("../test_account.toml");
+    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);
+
+    let mut gate_request = GateSwapRest::new(false, params);
+    let res_data = gate_request.wallet_fee().await;
+    if res_data.code == "200" {
+        let res_data_json: serde_json::Value = serde_json::from_str(&res_data.data).unwrap();
+        res_data_json["user_id"].to_string()
+    } else {
+        "".to_string()
+    }
+}
+
+// 测试订阅深度订阅
+#[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 gate_subscribe_type = vec![
+        GateSwapSubscribeType::PuFuturesOrderBook
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "depth").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 gate_subscribe_type = vec![
+        GateSwapSubscribeType::PuFuturesOrderBook
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "ticker").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 user_id = get_user_id().await;
+    trace!(?user_id);
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PrFuturesBalances(user_id)
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_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 user_id = get_user_id().await;
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PrFuturesPositions(user_id)
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "position").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 user_id = get_user_id().await;
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PrFuturesOrders(user_id)
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "orders").await;
+}

+ 279 - 0
standard/tests/gate_swap_test.rs

@@ -0,0 +1,279 @@
+mod exchange_test;
+
+use std::collections::BTreeMap;
+use std::env;
+use std::io::Error;
+use rust_decimal_macros::dec;
+use tokio::sync::mpsc;
+use tracing::{instrument, trace};
+use standard::exchange::{Exchange, ExchangeEnum};
+use standard::{Order, OrderCommand, Platform, utils};
+use crate::exchange_test::{test_new_exchange};
+
+const SYMBOL: &str = "BLZ_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_self_exchange = gate_swap_exchange.get_self_exchange();
+    trace!(?gate_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_self_symbol = gate_swap_exchange.get_self_symbol();
+    trace!(?gate_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_self_is_colo = gate_swap_exchange.get_self_is_colo();
+    trace!(?gate_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_self_params = gate_swap_exchange.get_self_params();
+    trace!("gate_get_self_params={:?}",gate_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_self_market = gate_swap_exchange.get_self_market();
+    trace!(?gate_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_request_delays = gate_swap_exchange.get_request_delays();
+    trace!(?gate_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_request_avg_delay = gate_swap_exchange.get_request_avg_delay();
+    trace!(?gate_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_request_max_delay = gate_swap_exchange.get_request_max_delay();
+    trace!(?gate_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_server_time = gate_swap_exchange.get_server_time().await;
+    trace!(?gate_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_account = gate_swap_exchange.get_account().await;
+    trace!(?gate_get_account);
+}
+
+// 测试获取持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_position() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_position = gate_swap_exchange.get_position().await;
+    trace!(?gate_get_position);
+}
+
+// 测试获取所有持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_positions() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_positions = gate_swap_exchange.get_positions().await;
+    trace!(?gate_get_positions);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_ticker = gate_swap_exchange.get_ticker().await;
+    trace!(?gate_get_ticker);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_market() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_market = gate_swap_exchange.get_market().await;
+    trace!(?gate_get_market);
+}
+
+// 测试获取Order详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_order_detail() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_order_detail = gate_swap_exchange.get_order_detail("", "999999").await;
+    trace!(?gate_get_order_detail);
+}
+
+// 测试获取Order列表信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_orders_list() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_get_orders_list = gate_swap_exchange.get_orders_list("finished").await;
+    trace!(?gate_get_orders_list);
+}
+
+// 测试下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_take_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_take_order = gate_swap_exchange.take_order("999999", "kk", dec!(0.27), dec!(100)).await;
+    trace!(?gate_take_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_cancel_order = gate_swap_exchange.cancel_order("", "999999").await;
+    trace!(?gate_cancel_order);
+}
+
+// 测试批量撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_cancel_orders = gate_swap_exchange.cancel_orders().await;
+    trace!(?gate_cancel_orders);
+}
+
+// 测试设置持仓模式
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_dual_mode() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_set_dual_mode = gate_swap_exchange.set_dual_mode("usdt", true).await;
+    trace!(?gate_set_dual_mode);
+}
+
+// 测试设置杠杆
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_dual_leverage() {
+    global::log_utils::init_log_with_trace();
+
+    let mut gate_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::GateSwap, SYMBOL).await;
+    let gate_set_dual_leverage = gate_swap_exchange.set_dual_leverage("10").await;
+    trace!(?gate_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("gate_access_key").unwrap_or("".to_string());
+    let secret_key = env::var("gate_secret_key").unwrap_or("".to_string());
+    params.insert("access_key".to_string(), access_key);
+    params.insert("secret_key".to_string(), secret_key);
+
+    let mut gate_swap_exchange: Box<dyn Platform> = Exchange::new(ExchangeEnum::GateSwap, 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()]);
+    gate_swap_exchange.command_order(command, Default::default()).await;
+
+    loop {
+        if let Ok(order) = order_receiver.try_recv() {
+            trace!(?order);
+        }
+        if let Ok(error) = error_receiver.try_recv() {
+            trace!(?error);
+        }
+    }
+}