Selaa lähdekoodia

项目整体结构梳理完毕。

skyffire 1 vuosi sitten
vanhempi
commit
f05676dc71
81 muutettua tiedostoa jossa 22158 lisäystä ja 0 poistoa
  1. 3 0
      exchanges/.gitignore
  2. 45 0
      exchanges/Cargo.toml
  3. 32 0
      exchanges/README.md
  4. 14 0
      exchanges/src/binance_spot_rest.rs
  5. 255 0
      exchanges/src/binance_spot_ws.rs
  6. 458 0
      exchanges/src/binance_swap_rest.rs
  7. 258 0
      exchanges/src/binance_swap_ws.rs
  8. 825 0
      exchanges/src/bitget_spot_rest.rs
  9. 346 0
      exchanges/src/bitget_spot_ws.rs
  10. 394 0
      exchanges/src/bitget_swap_rest.rs
  11. 310 0
      exchanges/src/bitget_swap_ws.rs
  12. 481 0
      exchanges/src/bybit_swap_rest.rs
  13. 347 0
      exchanges/src/bybit_swap_ws.rs
  14. 285 0
      exchanges/src/crypto_spot_ws.rs
  15. 15 0
      exchanges/src/gate_spot_rest.rs
  16. 15 0
      exchanges/src/gate_spot_ws.rs
  17. 614 0
      exchanges/src/gate_swap_rest.rs
  18. 359 0
      exchanges/src/gate_swap_ws.rs
  19. 102 0
      exchanges/src/http_tool.rs
  20. 521 0
      exchanges/src/kucoin_spot_rest.rs
  21. 378 0
      exchanges/src/kucoin_spot_ws.rs
  22. 625 0
      exchanges/src/kucoin_swap_rest.rs
  23. 397 0
      exchanges/src/kucoin_swap_ws.rs
  24. 28 0
      exchanges/src/lib.rs
  25. 580 0
      exchanges/src/okx_swap_rest.rs
  26. 371 0
      exchanges/src/okx_swap_ws.rs
  27. 110 0
      exchanges/src/proxy.rs
  28. 50 0
      exchanges/src/response_base.rs
  29. 426 0
      exchanges/src/socket_tool.rs
  30. 9 0
      exchanges/src/utils.rs
  31. 94 0
      exchanges/tests/binance_spot_test.rs
  32. 246 0
      exchanges/tests/binance_swap_test.rs
  33. 515 0
      exchanges/tests/bitget_spot_test.rs
  34. 145 0
      exchanges/tests/bitget_swap_test.rs
  35. 320 0
      exchanges/tests/bybit_swap_test.rs
  36. 86 0
      exchanges/tests/crypto_spot_test.rs
  37. 181 0
      exchanges/tests/gate_swap_test.rs
  38. 264 0
      exchanges/tests/kucoin_spot_test.rs
  39. 168 0
      exchanges/tests/kucoin_swap_test.rs
  40. 411 0
      exchanges/tests/okx_swap_test.rs
  41. 67 0
      exchanges/tests/socket_tool_test.rs
  42. 547 0
      exchanges/tests/test.rs
  43. 5 0
      standard/.gitignore
  44. 22 0
      standard/Cargo.toml
  45. 118 0
      standard/src/binance_spot.rs
  46. 51 0
      standard/src/binance_spot_handle.rs
  47. 393 0
      standard/src/binance_swap.rs
  48. 40 0
      standard/src/binance_swap_handle.rs
  49. 625 0
      standard/src/bitget_spot.rs
  50. 136 0
      standard/src/bitget_spot_handle.rs
  51. 671 0
      standard/src/bitget_swap.rs
  52. 181 0
      standard/src/bitget_swap_handle.rs
  53. 745 0
      standard/src/bybit_swap.rs
  54. 172 0
      standard/src/bybit_swap_handle.rs
  55. 110 0
      standard/src/exchange.rs
  56. 113 0
      standard/src/gate_spot.rs
  57. 755 0
      standard/src/gate_swap.rs
  58. 153 0
      standard/src/gate_swap_handle.rs
  59. 306 0
      standard/src/handle_info.rs
  60. 155 0
      standard/src/kucoin_handle.rs
  61. 738 0
      standard/src/kucoin_spot.rs
  62. 136 0
      standard/src/kucoin_spot_handle.rs
  63. 668 0
      standard/src/kucoin_swap.rs
  64. 575 0
      standard/src/lib.rs
  65. 198 0
      standard/src/okx_handle.rs
  66. 1072 0
      standard/src/okx_swap.rs
  67. 49 0
      standard/src/utils.rs
  68. 31 0
      standard/tests/binance_handle_test.rs
  69. 31 0
      standard/tests/binance_spot_handle_test.rs
  70. 172 0
      standard/tests/binance_swap_test.rs
  71. 56 0
      standard/tests/bitget_spot_handle_test.rs
  72. 195 0
      standard/tests/bitget_spot_test.rs
  73. 581 0
      standard/tests/exchange_test.rs
  74. 92 0
      standard/tests/gate_handle_test.rs
  75. 279 0
      standard/tests/gate_swap_test.rs
  76. 68 0
      standard/tests/kucoin_handle_test.rs
  77. 56 0
      standard/tests/kucoin_spot_handle_test.rs
  78. 145 0
      standard/tests/kucoin_spot_test.rs
  79. 271 0
      standard/tests/kucoin_swap_test.rs
  80. 68 0
      standard/tests/okx_handle_test.rs
  81. 229 0
      standard/tests/okx_swap_test.rs

+ 3 - 0
exchanges/.gitignore

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

+ 45 - 0
exchanges/Cargo.toml

@@ -0,0 +1,45 @@
+[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"

+ 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);
+```

+ 14 - 0
exchanges/src/binance_spot_rest.rs

@@ -0,0 +1,14 @@
+use std::collections::BTreeMap;
+
+#[derive(Clone)]
+pub struct BinanceSpotRest {}
+
+impl BinanceSpotRest {
+    pub fn new(_is_colo: bool, _login_param: BTreeMap<String, String>) -> BinanceSpotRest
+    {
+        return BinanceSpotRest::new_label("default-BinanceSpotRest".to_string(), _is_colo, _login_param);
+    }
+    pub fn new_label(_label: String, _is_colo: bool, _login_param: BTreeMap<String, String>) -> BinanceSpotRest {
+        BinanceSpotRest {}
+    }
+}

+ 255 - 0
exchanges/src/binance_spot_ws.rs

@@ -0,0 +1,255 @@
+// use std::sync::Arc;
+// use std::sync::atomic::AtomicBool;
+//
+// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+// use serde_json::json;
+// use tokio::sync::Mutex;
+// use tokio_tungstenite::tungstenite::{Error, Message};
+// use tracing::{info, trace};
+//
+// use crate::response_base::ResponseData;
+// use crate::socket_tool::AbstractWsMode;
+//
+// pub enum BinanceSpotWsType {
+//     //订阅频道类型
+//     PublicAndPrivate,
+// }
+//
+// //订阅频道
+// #[derive(Clone)]
+// pub enum BinanceSpotSubscribeType {
+//     PuBookTicker,
+//     PuAggTrade,
+//     PuDepth20levels100ms,
+// }
+//
+// //账号信息
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct BinanceSpotLogin {
+//     pub api_key: String,
+//     pub api_secret: String,
+// }
+//
+//
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct BinanceSpotWs {
+//     //类型
+//     label: String,
+//     //地址
+//     address_url: String,
+//     //账号信息
+//     login_param: Option<BinanceSpotLogin>,
+//     //币对
+//     symbol_s: Vec<String>,
+//     //订阅
+//     subscribe_types: Vec<BinanceSpotSubscribeType>,
+//     //心跳间隔
+//     heartbeat_time: u64,
+// }
+//
+// impl BinanceSpotWs {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//     pub fn new(is_colo: bool, login_param: Option<BinanceSpotLogin>, ws_type: BinanceSpotWsType) -> BinanceSpotWs {
+//         return BinanceSpotWs::new_label("default-BinanceSpotWs".to_string(), is_colo, login_param, ws_type);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: Option<BinanceSpotLogin>, ws_type: BinanceSpotWsType) -> BinanceSpotWs {
+//         /*******公共频道-私有频道数据组装*/
+//         let address_url = match ws_type {
+//             BinanceSpotWsType::PublicAndPrivate => {
+//                 "wss://stream.binance.com:9443/ws".to_string()
+//             }
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",address_url);
+//         } else {
+//             info!("走普通通道:{}",address_url);
+//         }
+//         /*****返回结构体*******/
+//         BinanceSpotWs {
+//             label,
+//             address_url,
+//             login_param,
+//             symbol_s: vec![],
+//             subscribe_types: vec![],
+//             heartbeat_time: 1000 * 20,
+//         }
+//     }
+//
+//     /*******************************************************************************************************/
+//     /*****************************************订阅函数********************************************************/
+//     /*******************************************************************************************************/
+//     //手动添加订阅信息
+//     pub fn set_subscribe(&mut self, subscribe_types: Vec<BinanceSpotSubscribeType>) {
+//         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_lowercase();
+//             // 字符串替换
+//             *symbol = symbol.replace("_", "");
+//             *symbol = symbol.replace("-", "");
+//         }
+//         self.symbol_s = b_array;
+//     }
+//     //频道是否需要登录
+//     fn contains_pr(&self) -> bool {
+//         for t in self.subscribe_types.clone() {
+//             if match t {
+//                 BinanceSpotSubscribeType::PuBookTicker => false,
+//                 BinanceSpotSubscribeType::PuAggTrade => false,
+//                 BinanceSpotSubscribeType::PuDepth20levels100ms => false,
+//             } {
+//                 return true;
+//             }
+//         }
+//         false
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************工具函数********************************************************/
+//     /*******************************************************************************************************/
+//     //订阅枚举解析
+//     pub fn enum_to_string(symbol: String, subscribe_type: BinanceSpotSubscribeType) -> String {
+//         match subscribe_type {
+//             BinanceSpotSubscribeType::PuAggTrade => {
+//                 format!("{}@aggTrade", symbol)
+//             }
+//             BinanceSpotSubscribeType::PuDepth20levels100ms => {
+//                 format!("{}@depth20@100ms", symbol)
+//             }
+//             BinanceSpotSubscribeType::PuBookTicker => {
+//                 format!("{}@bookTicker", symbol)
+//             }
+//         }
+//     }
+//     //订阅信息生成
+//     pub fn get_subscription(&self) -> String {
+//         let mut params = 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());
+//                 params.push(ty_str);
+//             }
+//         }
+//
+//         let str = json!({
+//             "method": "SUBSCRIBE",
+//             "params":  params,
+//             "id": 1
+//             });
+//         str.to_string()
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************socket基本*****************************************************/
+//     /*******************************************************************************************************/
+//     //链接
+//     pub async fn ws_connect_async(&mut self,
+//                                   is_shutdown_arc: Arc<AtomicBool>,
+//                                   _write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+//                                   write_rx: UnboundedReceiver<Message>,
+//                                   read_tx: UnboundedSender<ResponseData>) -> Result<(), Error>
+//     {
+//         let login_is = self.contains_pr();
+//         let subscription = self.get_subscription();
+//         let address_url = self.address_url.clone();
+//         let label = self.label.clone();
+//         // let heartbeat_time = self.heartbeat_time.clone();
+//
+//
+//         //心跳-- 方法内部线程启动
+//         // let write_tx_clone1 = Arc::clone(write_tx_am);
+//         // tokio::spawn(async move {
+//         //     trace!("线程-异步心跳-开始");
+//         //     AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Pong, heartbeat_time).await;
+//         //     trace!("线程-异步心跳-结束");
+//         // });
+//
+//         //设置订阅
+//         let mut subscribe_array = vec![];
+//         if login_is {
+//             //登录相关
+//         }
+//         subscribe_array.push(subscription.to_string());
+//
+//         //链接
+//         let t2 = tokio::spawn(async move {
+//             trace!("线程-异步链接-开始");
+//             match AbstractWsMode::ws_connect_async(is_shutdown_arc, address_url.clone(),
+//                                                    label.clone(), subscribe_array,
+//                                                    write_rx, read_tx,
+//                                                    Self::message_text,
+//                                                    Self::message_ping,
+//                                                    Self::message_pong,
+//             ).await {
+//                 Ok(_) => { trace!("线程-异步链接-结束"); }
+//                 Err(e) => { trace!("发生异常:币安-现货链接关闭-{:?}",e); }
+//             }
+//             trace!("线程-异步链接-结束");
+//         });
+//         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".to_string(), "success".to_string(), "pong".to_string()));
+//     }
+//     //数据解析-pong
+//     pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+//         return Option::from(ResponseData::new("".to_string(), "-301".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析
+//     pub fn ok_text(text: String) -> ResponseData {
+//         // trace!("原始数据");
+//         // trace!(?text);
+//         let mut res_data = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), "".to_string());
+//         let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+//
+//         if json_value.get("result").is_some() &&
+//             json_value.get("id").is_some()
+//         {
+//             //订阅反馈
+//             res_data.code = "-201".to_string();
+//             res_data.channel = "".to_string();
+//             res_data.message = "订阅成功!".to_string();
+//         } else if json_value.get("e").is_some() &&
+//             json_value["e"].as_str() == Option::from("aggTrade")
+//         {
+//             res_data.channel = "aggTrade".to_string();
+//             res_data.data = text;
+//         } else if json_value.get("u").is_some() &&
+//             json_value.get("s").is_some() &&
+//             json_value.get("b").is_some() &&
+//             json_value.get("B").is_some() &&
+//             json_value.get("a").is_some() &&
+//             json_value.get("A").is_some()
+//         {
+//             res_data.channel = "bookTicker".to_string();
+//             res_data.data = text;
+//         } else if json_value.get("bids").is_some() &&
+//             json_value.get("asks").is_some()
+//         {
+//             res_data.channel = "depth".to_string();
+//             res_data.data = text;
+//         } else {
+//             res_data.code = "".to_string();
+//             res_data.channel = "未知的频道".to_string();
+//         }
+//         res_data
+//     }
+// }

+ 458 - 0
exchanges/src/binance_swap_rest.rs

@@ -0,0 +1,458 @@
+use std::collections::BTreeMap;
+use std::str::FromStr;
+use reqwest::header::HeaderMap;
+use hex;
+use reqwest::Client;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use tracing::{error, info, trace};
+use ring::hmac;
+use serde_json::{json, Value};
+
+#[derive(Clone)]
+pub struct BinanceSwapRest {
+    label: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl BinanceSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BinanceSwapRest
+    {
+        return BinanceSwapRest::new_label("default-BinanceSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BinanceSwapRest
+    {
+        let base_url = if is_colo {
+            "https://fapi.binance.com".to_string()
+        } else {
+            "https://fapi.binance.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        BinanceSwapRest {
+            label,
+            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(),
+                                "".to_string(),
+                                format!("/fapi/v1/time"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取交易规则和交易对
+    pub async fn get_exchange_info(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/exchangeInfo"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    //账户信息
+    pub async fn get_account(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "recvWindow":"2000"
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v2/balance"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查询订单
+    pub async fn get_order(&mut self, symbol: String, order_id: i64, orig_client_order_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "symbol":symbol,
+            "recvWindow":"2000"
+         });
+        if order_id > 0 {
+            params["orderId"] = json!(order_id);
+        }
+        if orig_client_order_id.len() > 0 {
+            params["origClientOrderId"] = json!(orig_client_order_id);
+        }
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/order"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查看当前全部挂单
+    pub async fn get_open_orders(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "recvWindow":"2000"
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/openOrders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询所有订单(包括历史订单)
+    pub async fn get_all_orders(&mut self, symbol: String, limit: u64, start_time: i64, end_time: i64) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "limit":limit,
+            "startTime":start_time,
+            "endTime":end_time,
+            "recvWindow":"2000"
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/allOrders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //当前最优挂单
+    pub async fn get_book_ticker(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/ticker/bookTicker"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //用户持仓风险V2
+    pub async fn get_position_risk(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "recvWindow":"2000"
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v2/positionRisk"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //下单
+    pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/order"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //更改持仓模式
+    pub async fn change_pos_side(&mut self, dual_side_position: bool) -> ResponseData {
+        let dual_side_position_str = format!("{}", dual_side_position);
+        let params = serde_json::json!({
+            "dualSidePosition":dual_side_position_str,
+            "recvWindow":"2000"
+         });
+        let data = self.request("POST".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/positionSide/dual"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //撤销订单
+    pub async fn cancel_order(&mut self, symbol: String, order_id: i64, orig_client_order_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "symbol":symbol,
+            "recvWindow":"2000"
+         });
+        if order_id > 0 {
+            params["orderId"] = json!(order_id);
+        }
+        if orig_client_order_id.len() > 0 {
+            params["origClientOrderId"] = json!(orig_client_order_id);
+        }
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/order"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //根据币对 撤销全部订单
+    pub async fn cancel_order_all(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "recvWindow":"2000",
+         });
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/allOpenOrders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //账户成交历史
+    pub async fn get_user_trades(&mut self, symbol: String, start_time: i64, end_time: i64, limit: i64) -> ResponseData {
+        let mut params = serde_json::json!({
+            "symbol":symbol,
+            "limit":1000,
+            "recvWindow":"1000",
+         });
+        if start_time > 0 {
+            params["startTime"] = json!(start_time);
+        }
+        if end_time > 0 {
+            params["endTime"] = json!(end_time);
+        }
+        if limit > 0 {
+            params["limit"] = json!(limit);
+        }
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/userTrades"),
+                                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,
+                     request_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 params_json: serde_json::Value = serde_json::from_str(params.clone().as_str()).unwrap();
+        // let mut body = "".to_string();
+        let timestamp = chrono::Utc::now().timestamp_millis().to_string();
+        params_json.as_object_mut().unwrap().insert("timestamp".to_string(), serde_json::Value::from(timestamp));
+
+
+        let mut headers = HeaderMap::new();
+        if request_type == "GET" {
+            headers.insert("Content-Type", "application/json; charset=UTF-8".parse().unwrap());
+        } else if request_type == "POST" || request_type == "PUT" || request_type == "DELETE" {
+            headers.insert("Content-Type", "application/x-www-form-urlencoded".parse().unwrap());
+        }
+
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {//需要登录-且登录参数齐全
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      params_json.to_string(),
+                );
+                params_json.as_object_mut().unwrap().insert("signature".to_string(), serde_json::Value::from(sing.clone()));
+                // trace!("sing:{}", sing);
+                //组装header
+                headers.extend(Self::headers(access_key));
+            }
+        }
+
+        trace!("headers:{:?}", headers);
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            request_type.to_string(),
+            params_json.to_string(),
+            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) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("X-MBX-APIKEY", access_key.parse().unwrap());
+        headers
+    }
+    pub fn sign(secret_key: String, params_str: String) -> String
+    {
+        /*签名生成*/
+        let params_str_v = RestTool::parse_params_to_str(params_str.to_string());
+        let message = format!("{}", params_str_v);
+
+        // let secret_key2 = "2b5eb11e18796d12d88f13dc27dbbd02c2cc51ff7059765ed9821957d82bb4d9";
+        // let message2 = "symbol=BTCUSDT&side=BUY&type=LIMIT&quantity=1&price=9000&timeInForce=GTC&recvWindow=5000&timestamp=1591702613943";
+        let signed_key = hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_ref());
+        let sign = hex::encode(hmac::sign(&signed_key, message.as_bytes()).as_ref());
+
+        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 url_param = RestTool::parse_params_to_str(params.clone());
+        let addrs_url = format!("{}?{}", url.clone(), url_param);
+
+        let params_json: serde_json::Value = serde_json::from_str(&params).unwrap();
+        trace!("url:{}",url);
+        trace!("addrs_url:{}",addrs_url);
+        trace!("params_json:{}",params_json);
+        trace!("headers:{:?}",headers);
+        trace!("body:{:?}",params);
+
+
+        let request_builder = match request_type.as_str() {
+            "GET" => self.client.get(url.clone()).query(&params_json).headers(headers),
+            "POST" => self.client.post(url.clone()).query(&params_json).headers(headers),
+            "DELETE" => self.client.delete(url.clone()).query(&params_json).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.label.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 = data["msg"].as_str().unwrap();
+
+                let mut error = ResponseData::error(self.label.clone(), message.to_string());
+                error.code = i16::from_str(data["code"].as_str().unwrap()).unwrap();
+                error.message = format!("请求地址:{}, 请求参数:{}", base_url, params);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(), format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 258 - 0
exchanges/src/binance_swap_ws.rs

@@ -0,0 +1,258 @@
+use std::str::FromStr;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::{json, Value};
+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;
+
+//类型
+pub enum BinanceSwapWsType {
+    PublicAndPrivate,
+}
+
+//订阅频道
+#[derive(Clone)]
+pub enum BinanceSwapSubscribeType {
+    PuBookTicker,
+    PuAggTrade,
+    PuDepth20levels100ms,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BinanceSwapLogin {
+    pub api_key: String,
+    pub api_secret: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BinanceSwapWs {
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<BinanceSwapLogin>,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<BinanceSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl BinanceSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
+        return BinanceSwapWs::new_label("default-BinanceSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            BinanceSwapWsType::PublicAndPrivate => {
+                // "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms".to_string(),
+                "wss://fstream.binance.com/stream".to_string()
+            }
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        BinanceSwapWs {
+            label,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 20,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BinanceSwapSubscribeType>) {
+        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_lowercase();
+            // 字符串替换
+            *symbol = symbol.replace("_", "");
+            *symbol = symbol.replace("-", "");
+        }
+        self.symbol_s = b_array;
+    }
+    //频道是否需要登录
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                BinanceSwapSubscribeType::PuBookTicker => false,
+                BinanceSwapSubscribeType::PuAggTrade => false,
+                BinanceSwapSubscribeType::PuDepth20levels100ms => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: BinanceSwapSubscribeType) -> String {
+        match subscribe_type {
+            BinanceSwapSubscribeType::PuAggTrade => {
+                format!("{}@aggTrade", symbol)
+            }
+            BinanceSwapSubscribeType::PuDepth20levels100ms => {
+                format!("{}@depth20@100ms", symbol)
+            }
+            BinanceSwapSubscribeType::PuBookTicker => {
+                format!("{}@bookTicker", symbol)
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> String {
+        let mut params = 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());
+                params.push(ty_str);
+            }
+        }
+
+        let str = json!({
+            "method": "SUBSCRIBE",
+            "params":  params,
+            "id": 1
+            });
+        str.to_string()
+    }
+    /*******************************************************************************************************/
+    /*****************************************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 label = self.label.clone();
+        // let heartbeat_time = self.heartbeat_time.clone();
+
+
+        //心跳-- 方法内部线程启动
+        // let write_tx_clone1 = Arc::clone(write_tx_am);
+        // tokio::spawn(async move {
+        //     trace!("线程-异步心跳-开始");
+        //     AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Pong, heartbeat_time).await;
+        //     trace!("线程-异步心跳-结束");
+        // });
+
+        //设置订阅
+        let mut subscribe_array = vec![];
+        if login_is {
+            //登录相关
+        }
+        subscribe_array.push(subscription.to_string());
+
+        //链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            loop {
+                info!("binance_usdt_swap socket 连接中……");
+                // ws层重连
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, label.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong).await;
+
+                error!("binance_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::String("pong".to_string())));
+    }
+    //数据解析-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 ok_text(text: String) -> ResponseData {
+        // trace!("原始数据");
+        // 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.get("result").is_some() && json_value.get("id").is_some() &&
+            json_value.get("id").unwrap() == 1
+        {
+            res_data.code = -201;
+            res_data.message = "订阅成功".to_string();
+        } else if json_value.get("error").is_some() {//订阅返回
+            res_data.code = i16::from_str(json_value["error"]["code"].as_str().unwrap()).unwrap();
+            res_data.message = json_value["error"]["msg"].to_string();
+        } else if json_value.get("stream").is_some() {//订阅返回
+            res_data.data = json_value["data"].clone();
+            res_data.code = 200;
+
+            let channel = json_value["stream"].as_str().unwrap();
+            if channel.contains("@aggTrade") {
+                res_data.channel = "aggTrade".to_string();
+            } else if channel.contains("@depth20@100ms") {
+                res_data.channel = "depth".to_string();
+            } else if channel.contains("@bookTicker") {
+                res_data.channel = "bookTicker".to_string();
+            } else {
+                res_data.code = -1;
+                res_data.channel = "未知的频道".to_string();
+            }
+        } else {
+            res_data.code = -1;
+            res_data.channel = "未知的频道2".to_string();
+        }
+        res_data
+    }
+}

+ 825 - 0
exchanges/src/bitget_spot_rest.rs

@@ -0,0 +1,825 @@
+// use std::collections::BTreeMap;
+// use reqwest::header::HeaderMap;
+// use reqwest::{Client};
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::FromPrimitive;
+// use rust_decimal_macros::dec;
+// use tracing::{info, trace};
+// use crate::http_tool::RestTool;
+// use crate::response_base::ResponseData;
+// use ring::hmac;
+// use serde_json::Value;
+//
+// #[derive(Clone, Debug)]
+// pub struct BitgetSpotRest {
+//     pub label: String,
+//     base_url: String,
+//     client: reqwest::Client,
+//     /*******参数*/
+//     //是否需要登录
+//     //登录所需参数
+//     login_param: BTreeMap<String, String>,
+//     delays: Vec<i64>,
+//     max_delay: i64,
+//     avg_delay: Decimal,
+//
+// }
+//
+// impl BitgetSpotRest {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//
+//     pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BitgetSpotRest
+//     {
+//         return BitgetSpotRest::new_label("default-BitgetSpotRest".to_string(), is_colo, login_param);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BitgetSpotRest {
+//         let base_url = if is_colo {
+//             "https://api.bitget.com".to_string()
+//         } else {
+//             "https://api.bitget.com".to_string()
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",base_url);
+//         } else {
+//             info!("走普通通道:{}",base_url);
+//         }
+//         /*****返回结构体*******/
+//         BitgetSpotRest {
+//             label,
+//             base_url,
+//             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/v2".to_string(),
+//                                 "/public/time".to_string(),
+//                                 false,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //账户信息
+//     pub async fn get_account_info(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/account/info".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取账户币种资产
+//     pub async fn get_account_assets(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/account/assets".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取币种信息
+//     pub async fn get_coins(&mut self, coin: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "coin":coin
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/public/coins".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取交易对信息
+//     pub async fn get_symbols(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/public/symbols".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取现货VIP费率
+//     pub async fn get_vip_fee_rate(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/vip-fee-rate".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取行情信息
+//     pub async fn get_tickers(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/tickers".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取合并交易深度
+//     pub async fn get_merge_depth(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/merge-depth".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取K线数据
+//     //参数:granularity: K线的时间颗粒度
+//     //                   分钟:1min,5min,15min,30min
+//     //                   小时:1h,4h,6h,12h
+//     //                   天:1day,3day
+//     //                   周:1week
+//     //                   月:1M
+//     //                   零时区小时线:6Hutc,12Hutc
+//     //                   零时区日线:1Dutc ,3Dutc
+//     //                   零时区周线:1Wutc
+//     //                   零时区月线:1Mutc
+//     //                   1m、3m、5m可以查一个月 ;15m可以查52天; 30m查62天; 1H可以查83天; 2H可以查120天; 4H可以查240天; 6H可以查360天
+//     //start_time :K线数据的时间起始点,即获取该时间戳以后的K线数据 Unix毫秒时间戳,例如1690196141868
+//     //end_time :K线数据的时间终止点,即获取该时间戳以前的K线数据 Unix毫秒时间戳,例如1690196141868
+//     //limit :查询条数  默认100,最大1000
+//     pub async fn get_candles(&mut self, symbol: String, granularity: String, start_time: String, end_time: String, limit: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol,
+//             "granularity":granularity,
+//             "startTime":start_time,
+//             "endTime":end_time,
+//             "limit":limit,
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/candles".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取历史K线数据
+//     pub async fn get_history_candles(&mut self, symbol: String, granularity: String, end_time: String, limit: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol,
+//             "granularity":granularity,
+//             "endTime":end_time,
+//             "limit":limit,
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/history-candles".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取最近成交数据
+//     pub async fn get_market_fills(&mut self, symbol: String, limit: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol,
+//             "limit":limit,
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/fills".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取历史成交数据
+//     pub async fn get_market_fills_history(&mut self, symbol: String, start_time: String, end_time: String, limit: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//             "limit": "1000"
+//          });
+//         if start_time.len() > 0 {
+//             params["startTime"] = serde_json::json!(start_time);
+//         }
+//         if end_time.len() > 0 {
+//             params["endTime"] = serde_json::json!(end_time);
+//         }
+//         if limit.len() > 0 {
+//             params["limit"] = serde_json::json!(limit);
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/market/fills-history".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //下单
+//     pub async fn spot_order(&mut self, params: serde_json::Value) -> ResponseData {
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/place-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //批量下单
+//     pub async fn spot_orders(&mut self, params: serde_json::Value) -> ResponseData {
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/batch-orders".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //撤单
+//     pub async fn spot_cancel_order(&mut self, symbol: String, order_id: String, client_oid: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//          });
+//         if order_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+//         }
+//         if client_oid.len() > 0 {
+//             params.as_object_mut().unwrap().insert("clientOid".parse().unwrap(), serde_json::Value::from(client_oid));
+//         }
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/cancel-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //批量撤单
+//     pub async fn spot_cancel_orders(&mut self, symbol: String, order_list: Vec<Value>) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol,
+//             "orderList":order_list,
+//          });
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/batch-cancel-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //按币对撤单
+//     pub async fn spot_cancel_symbol_orders(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol,
+//          });
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/cancel-symbol-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取订单详情
+//     pub async fn get_order(&mut self, order_id: String, client_oid: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "orderId":order_id,
+//             "clientOid":client_oid,
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/orderInfo".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取当前委托列表
+//     pub async fn get_unfilled_orders(&mut self,
+//                                      symbol: String,
+//                                      start_time: String,
+//                                      end_time: String,
+//                                      id_less_than: String,
+//                                      limit: String,
+//                                      order_id: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//          });
+//         if start_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("startTime".parse().unwrap(), serde_json::Value::from(start_time));
+//         }
+//         if end_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("endTime".parse().unwrap(), serde_json::Value::from(end_time));
+//         }
+//         if id_less_than.len() > 0 {
+//             params.as_object_mut().unwrap().insert("idLessThan".parse().unwrap(), serde_json::Value::from(id_less_than));
+//         }
+//         if limit.len() > 0 {
+//             params.as_object_mut().unwrap().insert("limit".parse().unwrap(), serde_json::Value::from(limit));
+//         }
+//         if order_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/unfilled-orders".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取历史委托列表
+//     pub async fn get_history_orders(&mut self,
+//                                     symbol: String,
+//                                     start_time: String,
+//                                     end_time: String,
+//                                     id_less_than: String,
+//                                     limit: String,
+//                                     order_id: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//          });
+//         if start_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("startTime".parse().unwrap(), serde_json::Value::from(start_time));
+//         }
+//         if end_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("endTime".parse().unwrap(), serde_json::Value::from(end_time));
+//         }
+//         if id_less_than.len() > 0 {
+//             params.as_object_mut().unwrap().insert("idLessThan".parse().unwrap(), serde_json::Value::from(id_less_than));
+//         }
+//         if limit.len() > 0 {
+//             params.as_object_mut().unwrap().insert("limit".parse().unwrap(), serde_json::Value::from(limit));
+//         }
+//         if order_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/history-orders".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //获取成交明细
+//     pub async fn get_fills(&mut self,
+//                            symbol: String,
+//                            order_id: String,
+//                            start_time: String,
+//                            end_time: String,
+//                            limit: String,
+//                            id_less_than: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//          });
+//         if order_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+//         }
+//         if start_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("startTime".parse().unwrap(), serde_json::Value::from(start_time));
+//         }
+//         if end_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("endTime".parse().unwrap(), serde_json::Value::from(end_time));
+//         }
+//         if limit.len() > 0 {
+//             params.as_object_mut().unwrap().insert("limit".parse().unwrap(), serde_json::Value::from(limit));
+//         }
+//         if id_less_than.len() > 0 {
+//             params.as_object_mut().unwrap().insert("idLessThan".parse().unwrap(), serde_json::Value::from(id_less_than));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/fills".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //下单计划委托
+//     pub async fn spot_place_plan_order(&mut self, params: serde_json::Value) -> ResponseData {
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/place-plan-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //修改计划委托
+//     pub async fn update_place_plan_order(&mut self, params: serde_json::Value) -> ResponseData {
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/modify-plan-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //撤销计划委托
+//     pub async fn cancel_plan_order(&mut self, order_id: String, client_oid: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//          });
+//         if order_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+//         }
+//         if client_oid.len() > 0 {
+//             params.as_object_mut().unwrap().insert("clientOid".parse().unwrap(), serde_json::Value::from(client_oid));
+//         }
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/cancel-plan-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取当前计划委托
+//     pub async fn get_current_plan_order(&mut self,
+//                                         symbol: String,
+//                                         limit: String,
+//                                         id_less_than: String,
+//                                         start_time: String,
+//                                         end_time: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//             "limit":limit,
+//          });
+//
+//
+//         if id_less_than.len() > 0 {
+//             params.as_object_mut().unwrap().insert("idLessThan".parse().unwrap(), serde_json::Value::from(id_less_than));
+//         }
+//         if start_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("startTime".parse().unwrap(), serde_json::Value::from(start_time));
+//         }
+//         if end_time.len() > 0 {
+//             params.as_object_mut().unwrap().insert("endTime".parse().unwrap(), serde_json::Value::from(end_time));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/current-plan-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取历史计划委托
+//     pub async fn get_history_plan_order(&mut self,
+//                                         symbol: String,
+//                                         start_time: String,
+//                                         end_time: String,
+//                                         limit: String,
+//     ) -> ResponseData {
+//         let params = serde_json::json!({
+//             "symbol":symbol,
+//             "startTime":start_time,
+//             "endTime":end_time,
+//             "limit":limit,
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/history-plan-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //批量撤销计划委托
+//     pub async fn cancel_plan_orders(&mut self, symbol_list: Vec<String>) -> ResponseData {
+//         let mut params = serde_json::json!({
+//          });
+//         if symbol_list.len() > 0 {
+//             params.as_object_mut().unwrap().insert("symbolList".parse().unwrap(), serde_json::Value::from(symbol_list));
+//         }
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/trade/batch-cancel-plan-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //划转
+//     pub async fn wallet_transfer(&mut self,
+//                                  from_type: String,
+//                                  to_type: String,
+//                                  amount: String,
+//                                  coin: String,
+//                                  symbol: String,
+//                                  client_oid: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//                    "fromType":from_type ,
+//                    "toType":to_type ,
+//                    "amount":amount ,
+//                    "coin":coin ,
+//          });
+//         if symbol.len() > 0 {
+//             params.as_object_mut().unwrap().insert("symbol".parse().unwrap(), serde_json::Value::from(symbol));
+//         }
+//         if client_oid.len() > 0 {
+//             params.as_object_mut().unwrap().insert("clientOid".parse().unwrap(), serde_json::Value::from(client_oid));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/wallet/transfer".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取账单流水
+//     pub async fn get_account_bills(&mut self,
+//                                    coin: String,
+//                                    limit: String,
+//                                    start_time: String,
+//                                    end_time: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//                    "coin":coin ,
+//                    "groupType":"transaction" ,
+//                    "limit":limit ,
+//
+//          });
+//         if start_time.len() > 0 {
+//             params["startTime"] = serde_json::json!(start_time);
+//         }
+//         if end_time.len() > 0 {
+//             params["endTime"] = serde_json::json!(end_time);
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 "/spot/account/bills".to_string(),
+//                                 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();
+//     }
+//     //调用请求
+//     pub async fn request(&mut self,
+//                          method: 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();
+//         let mut passphrase = "".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();
+//         }
+//         if self.login_param.contains_key("pass_key") {
+//             passphrase = self.login_param.get("pass_key").unwrap().to_string();
+//         }
+//         let mut is_login_param = true;
+//         if access_key == "" || secret_key == "" || passphrase == "" {
+//             is_login_param = false
+//         }
+//
+//
+//         //请求头配置-如果需要登录则存在额外配置
+//         let mut body = "".to_string();
+//         let timestamp = Self::get_timestamp();
+//         let mut headers = HeaderMap::new();
+//         headers.insert("Content-Type", "application/json".parse().unwrap());
+//         headers.insert("locale", "en-US".parse().unwrap());
+//         if method == "POST" {
+//             body = params.clone();
+//         }
+//
+//
+//         //是否需要登录-- 组装sing
+//         if is_login {
+//             if !is_login_param {
+//                 let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+//                 return e;
+//             } else {
+//                 //需要登录-且登录参数齐全
+//                 trace!("param:{}", params);
+//                 trace!("body:{}", body);
+//                 //组装sing
+//                 let sing = Self::sign(secret_key.clone(),
+//                                       method.clone(),
+//                                       prefix_url.clone(),
+//                                       request_url.clone(),
+//                                       params.clone(),
+//                                       body.clone(),
+//                                       timestamp.clone(),
+//                 );
+//                 //组装header
+//                 headers.extend(Self::headers(sing, timestamp, passphrase, access_key));
+//             }
+//         }
+//
+//
+//         // trace!("headers:{:?}", headers);
+//         let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+//         let start_time = chrono::Utc::now().timestamp_millis();
+//         let get_response = self.http_tool(
+//             format!("{}{}", prefix_url.clone(), request_url.clone()),
+//             method.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();
+//         let res_data = Self::res_data_analysis(get_response, base_url, params);
+//         res_data
+//     }
+//
+//     pub fn headers(sign: String, timestamp: String, passphrase: String, access_key: String) -> HeaderMap {
+//         let mut headers = HeaderMap::new();
+//         headers.insert("ACCESS-KEY", access_key.parse().unwrap());
+//         headers.insert("ACCESS-SIGN", sign.parse().unwrap());
+//         headers.insert("ACCESS-TIMESTAMP", timestamp.parse().unwrap());
+//         headers.insert("ACCESS-PASSPHRASE", passphrase.parse().unwrap());
+//         headers
+//     }
+//     pub fn sign(secret_key: String,
+//                 method: String, prefix_url: String, request_url: String,
+//                 params: String, body: String, timestamp: String) -> String
+//     {
+//         /*签名生成*/
+//         let url_param_str = RestTool::parse_params_to_str(params);
+//         let base_url = if method == "GET" && url_param_str.len() > 0 {
+//             format!("{}{}?{}", prefix_url, request_url, url_param_str)
+//         } else {
+//             format!("{}{}", prefix_url, request_url)
+//         };
+//
+//         // 时间戳 + 请求类型+ 请求参数字符串
+//         let message = format!("{}{}{}{}", timestamp, method, base_url, body);
+//         trace!("message:{}",message);
+//
+//         // 做签名
+//         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);
+//         sign
+//     }
+//
+//     async fn http_tool(&mut 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: String = if RestTool::parse_params_to_str(params.clone()) == "" {
+//             url.clone()
+//         } else {
+//             format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()))
+//         };
+//         trace!("url:{}", url);
+//         trace!("addrs_url:{}", addrs_url);
+//         let params_json: serde_json::Value = serde_json::from_str(&params).unwrap();
+//         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(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(self.label.clone(), 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(self.label.clone(), "200".to_string(), "success".to_string(), body);
+//         } else {
+//             let body = response.text().await?;
+//             // trace!("error-----{}", body);
+//             res_data = ResponseData::error(self.label.clone(), body.to_string())
+//         }
+//
+//         Ok(res_data)
+//     }
+//
+//
+//     //res_data 解析
+//     pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
+//         // trace!("原始数据:{:?}",result);
+//         match result {
+//             Ok(res_data) => {
+//                 if res_data.code != "200" {
+//                     // trace!("不等于200");
+//                     let message: String = res_data.message;
+//                     let json_value: serde_json::Value = serde_json::from_str(&message).unwrap();
+//                     let code = json_value["code"].as_str().unwrap();
+//                     let msg = json_value["msg"].as_str().unwrap();
+//                     let error = ResponseData::new("".to_string(),
+//                                                   format!("{}", code),
+//                                                   format!("{}", msg),
+//                                                   format!("请求地址:{},请求参数:{}", base_url, params));
+//                     error
+//                 } else {
+//                     // trace!("等于200");
+//                     let body: String = res_data.data;
+//                     let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+//
+//                     let code = json_value["code"].as_str().unwrap();
+//                     if code == "00000" {
+//                         let data = serde_json::to_string(&json_value["data"]).unwrap();
+//                         let success = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), data.parse().unwrap());
+//                         success
+//                     } else {
+//                         let msg = json_value["msg"].as_str().unwrap();
+//                         // trace!("发生错误:??{:?}",data_json.to_string());
+//                         let error = ResponseData::new("".to_string(),
+//                                                       format!("{}", code),
+//                                                       format!("{}", msg),
+//                                                       format!("请求地址:{},请求参数:{}", base_url, params));
+//                         error
+//                     }
+//                 }
+//             }
+//             Err(err) => {
+//                 let error = ResponseData::error("".to_string(), format!("json 解析失败:{}", err));
+//                 error
+//             }
+//         }
+//     }
+//     fn get_timestamp() -> String {
+//         chrono::Utc::now().timestamp_millis().to_string()
+//     }
+// }

+ 346 - 0
exchanges/src/bitget_spot_ws.rs

@@ -0,0 +1,346 @@
+// use std::sync::Arc;
+// use std::sync::atomic::AtomicBool;
+// use std::time::Duration;
+//
+// use chrono::Utc;
+// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+// use ring::hmac;
+// use serde_json::{json, Value};
+// use tokio::sync::Mutex;
+// use tokio_tungstenite::tungstenite::{Error, Message};
+// use tracing::{info, trace};
+//
+// use crate::response_base::ResponseData;
+// use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+//
+// //类型
+// pub enum BitgetSpotWsType {
+//     Public,
+//     Private,
+// }
+//
+// //订阅频道
+// #[derive(Clone)]
+// pub enum BitgetSpotSubscribeType {
+//     PuTicker,
+//     PuCandle1m,
+//     PuTrade,
+//     PuBooks5,
+//
+//     PrAccount,
+//     PrOrders,
+// }
+//
+// //账号信息
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct BitgetSpotLogin {
+//     pub api_key: String,
+//     pub secret_key: String,
+//     pub passphrase_key: String,
+// }
+//
+//
+// #[derive(Clone)]
+// pub struct BitgetSpotWs {
+//     //类型
+//     label: String,
+//     //地址
+//     address_url: String,
+//     //账号
+//     login_param: Option<BitgetSpotLogin>,
+//     //币对
+//     symbol_s: Vec<String>,
+//     //订阅
+//     subscribe_types: Vec<BitgetSpotSubscribeType>,
+//     //心跳间隔
+//     heartbeat_time: u64,
+// }
+//
+// impl BitgetSpotWs {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//     pub fn new(is_colo: bool, login_param: Option<BitgetSpotLogin>, ws_type: BitgetSpotWsType) -> BitgetSpotWs {
+//         return BitgetSpotWs::new_label("default-BitgetSpotWs".to_string(), is_colo, login_param, ws_type);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: Option<BitgetSpotLogin>, ws_type: BitgetSpotWsType) -> BitgetSpotWs
+//     {
+//         /*******公共频道-私有频道数据组装*/
+//         let address_url = match ws_type {
+//             BitgetSpotWsType::Public => {
+//                 format!("wss://ws.bitget.com/v2/ws/public")
+//             }
+//             BitgetSpotWsType::Private => {
+//                 format!("wss://ws.bitget.com/v2/ws/private")
+//             }
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",address_url);
+//         } else {
+//             info!("走普通通道:{}",address_url);
+//         }
+//
+//         BitgetSpotWs {
+//             label,
+//             address_url,
+//             login_param,
+//             symbol_s: vec![],
+//             subscribe_types: vec![],
+//             heartbeat_time: 1000 * 10,
+//         }
+//     }
+//
+//     /*******************************************************************************************************/
+//     /*****************************************订阅函数********************************************************/
+//     /*******************************************************************************************************/
+//     //手动添加订阅信息
+//     pub fn set_subscribe(&mut self, subscribe_types: Vec<BitgetSpotSubscribeType>) {
+//         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("-", "");
+//             *symbol = symbol.replace("_", "");
+//         }
+//         self.symbol_s = b_array;
+//     }
+//     //频道是否需要登录
+//     fn contains_pr(&self) -> bool {
+//         for t in self.subscribe_types.clone() {
+//             if match t {
+//                 BitgetSpotSubscribeType::PuTicker => false,
+//                 BitgetSpotSubscribeType::PuCandle1m => false,
+//                 BitgetSpotSubscribeType::PuTrade => false,
+//                 BitgetSpotSubscribeType::PuBooks5 => false,
+//
+//                 BitgetSpotSubscribeType::PrAccount => false,
+//                 BitgetSpotSubscribeType::PrOrders => false
+//             } {
+//                 return true;
+//             }
+//         }
+//         false
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************工具函数********************************************************/
+//     /*******************************************************************************************************/
+//     //订阅枚举解析
+//     pub fn enum_to_string(symbol: String, subscribe_type: BitgetSpotSubscribeType) -> Value {
+//         match subscribe_type {
+//             BitgetSpotSubscribeType::PuTicker => {
+//                 json!({
+//                     "instType": "SPOT",
+//                     "channel": "ticker",
+//                     "instId": symbol,
+//                 })
+//             }
+//             BitgetSpotSubscribeType::PuCandle1m => {
+//                 json!({
+//                     "instType": "SPOT",
+//                     "channel": "candle1m",
+//                     "instId": symbol,
+//                 })
+//             }
+//             BitgetSpotSubscribeType::PuTrade => {
+//                 json!({
+//                     "instType": "SPOT",
+//                     "channel": "trade",
+//                     "instId": symbol,
+//                 })
+//             }
+//             BitgetSpotSubscribeType::PuBooks5 => {
+//                 json!({
+//                     "instType": "SPOT",
+//                     "channel": "books5",
+//                     "instId": symbol,
+//                 })
+//             }
+//             BitgetSpotSubscribeType::PrAccount => {
+//                 json!({
+//                     "instType": "SPOT",
+//                     "channel": "account",
+//                     "coin": "default"
+//                 })
+//             }
+//             BitgetSpotSubscribeType::PrOrders => {
+//                 json!({
+//                     "instType": "SPOT",
+//                     "channel": "orders",
+//                     "instId": symbol
+//                 })
+//             }
+//         }
+//     }
+//     //订阅信息生成
+//     pub fn get_subscription(&self) -> String {
+//         let mut params = 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());
+//                 params.push(ty_str);
+//             }
+//         }
+//         let str = json!({
+//             "op": "subscribe",
+//             "args": params
+//         });
+//
+//         str.to_string()
+//     }
+//     //登录数据组装
+//     fn log_in_to_str(&self) -> String {
+//         let mut login_json_str = "".to_string();
+//
+//         let mut access_key: String = "".to_string();
+//         let mut secret_key: String = "".to_string();
+//         let mut passphrase: String = "".to_string();
+//         match self.login_param.clone() {
+//             None => {}
+//             Some(param) => {
+//                 access_key = param.api_key;
+//                 secret_key = param.secret_key;
+//                 passphrase = param.passphrase_key;
+//             }
+//         }
+//         if access_key.len() > 0 || secret_key.len() > 0 || passphrase.len() > 0 {
+//             let timestamp = Utc::now().timestamp().to_string();
+//             // 时间戳 + 请求类型+ 请求参数字符串
+//             let message = format!("{}GET{}", timestamp, "/user/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
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************socket基本*****************************************************/
+//     /*******************************************************************************************************/
+//     //链接
+//     pub async fn ws_connect_async(&mut self,
+//                                   is_shutdown_arc: Arc<AtomicBool>,
+//                                   write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+//                                   write_rx: UnboundedReceiver<Message>,
+//                                   read_tx: UnboundedSender<ResponseData>) -> Result<(), Error>
+//     {
+//         let login_is = self.contains_pr();
+//         let subscription = self.get_subscription();
+//         let address_url = self.address_url.clone();
+//         let label = self.label.clone();
+//         let heartbeat_time = self.heartbeat_time.clone();
+//
+//
+//         //心跳-- 方法内部线程启动
+//         let write_tx_clone1 = Arc::clone(write_tx_am);
+//         tokio::spawn(async move {
+//             trace!("线程-异步心跳-开始");
+//             AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time).await;
+//             trace!("线程-异步心跳-结束");
+//         });
+//
+//         //设置订阅
+//         let mut subscribe_array = vec![];
+//         if login_is {
+//             //登录相关
+//             let login_str = self.log_in_to_str();
+//             let write_tx_clone2 = Arc::clone(write_tx_am);
+//             AbstractWsMode::send_subscribe(write_tx_clone2, Message::Text(login_str)).await;
+//             tokio::time::sleep(Duration::from_millis(1000 * 3)).await;
+//         }
+//         subscribe_array.push(subscription.to_string());
+//
+//         //链接
+//         let t2 = tokio::spawn(async move {
+//             trace!("线程-异步链接-开始");
+//             match AbstractWsMode::ws_connect_async(is_shutdown_arc, address_url.clone(),
+//                                                    label.clone(), subscribe_array,
+//                                                    write_rx, read_tx,
+//                                                    Self::message_text,
+//                                                    Self::message_ping,
+//                                                    Self::message_pong,
+//             ).await {
+//                 Ok(_) => { trace!("线程-异步链接-结束"); }
+//                 Err(e) => { trace!("发生异常:bitget-现货链接关闭-{:?}",e); }
+//             }
+//             trace!("线程-异步链接-结束");
+//         });
+//         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".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析-pong
+//     pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+//         return Option::from(ResponseData::new("".to_string(), "-301".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析
+//     pub fn ok_text(text: String) -> ResponseData
+//     {
+//         // trace!("原始数据:{}", text);
+//         // trace!("大小:{}", text.capacity());
+//         let mut res_data = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), "".to_string());
+//         let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+//
+//         // {"event":"login","code":0}
+//         if json_value.get("event").is_some() && json_value["event"].as_str() == Option::from("login") {
+//             if json_value.get("code").is_some() && json_value["code"] == 0 {
+//                 res_data.message = format!("登录成功");
+//             } else {
+//                 res_data.message = format!("登录失败:({},{})", json_value.get("code").as_ref().unwrap(), json_value.get("msg").as_ref().unwrap());
+//             }
+//             res_data.channel = format!("login");
+//             res_data.code = "-200".to_string();
+//             res_data
+//         } else if json_value.get("event").is_some() && json_value["event"].as_str() == Option::from("subscribe") {
+//             // trace!("解析-订阅反馈:{}", text);
+//             res_data.code = "-201".to_string();
+//             res_data.data = text;
+//             res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+//             res_data
+//         } else if json_value.get("action").is_some() {
+//             // trace!("解析-推送数据:{}", text);
+//             res_data.data = json_value["data"].to_string();
+//             if res_data.data == "[]" {
+//                 res_data.code = "".to_string();
+//             } else {
+//                 res_data.code = "200".to_string();
+//             }
+//             res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+//             res_data.reach_time = json_value["ts"].as_i64().unwrap() * 1000;
+//             res_data
+//         } else {
+//             res_data
+//         }
+//     }
+// }

+ 394 - 0
exchanges/src/bitget_swap_rest.rs

@@ -0,0 +1,394 @@
+use std::collections::BTreeMap;
+use std::str::FromStr;
+use reqwest::Client;
+use reqwest::header::HeaderMap;
+use rust_decimal::Decimal;
+use tracing::info;
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::Value;
+
+#[derive(Clone, Debug)]
+pub struct BitgetSwapRest {
+    pub label: String,
+    base_url: String,
+    client: Client,
+    login_param: BTreeMap<String, String>,                  // 登录参数
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl BitgetSwapRest {
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BitgetSwapRest {
+        return BitgetSwapRest::new_label("default-BitgetSwapRest".to_string(), is_colo, login_param)
+    }
+
+    // 构造Bitget,可以自定义label
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BitgetSwapRest {
+        let base_url = if is_colo {
+            "https://api.bitget.com".to_string()
+        } else {
+            "https://api.bitget.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道", base_url);
+        } else {
+            info!("走普通通道:{}", base_url);
+        }
+
+        BitgetSwapRest {
+            label,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: Decimal::ZERO,
+        }
+    }
+
+    //获取行情信息
+    pub async fn get_contracts(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol": symbol,
+            "productType": "USDT-FUTURES"
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v2".to_string(),
+                                "/mix/market/contracts".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //获取行情信息
+    pub async fn get_tickers(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol": symbol,
+            "productType": "USDT-FUTURES"
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v2".to_string(),
+                                "/mix/market/ticker".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    // 获取服务器时间
+    pub async fn get_server_time(&mut self) -> ResponseData {
+        let params = serde_json::json!({});
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/public/time".to_string(),
+                     false,
+                     params.to_string(),
+        ).await
+    }
+
+    // 获取账户信息
+    pub async fn get_account_info(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "productType": "USDT-FUTURES"
+        });
+
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/account/accounts".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 获取仓位信息(单个)
+    pub async fn get_single_position(&mut self, params: Value) -> ResponseData {
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/position/single-position".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 获取所有仓位
+    pub async fn get_all_position(&mut self, params: Value) -> ResponseData {
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/position/all-position".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 下单
+    pub async fn swap_order(&mut self, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/order/place-order".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 撤单
+    pub async fn cancel_order(&mut self, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/order/cancel-order".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 获取订单详情
+    pub async fn get_order(&mut self, params: Value) -> ResponseData {
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/order/detail".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 获取当前未成交订单
+    pub async fn get_pending_orders(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "productType": "USDT-FUTURES"
+        });
+
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/order/orders-pending".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 调整杠杆
+    pub async fn set_leverage(&mut self, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/account/set-leverage".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 调整持仓模式(单向、双向)
+    pub async fn set_position_mode(&mut self, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/account/set-position-mode".to_string(),
+                     true,
+                     params.to_string(),
+        ).await
+    }
+
+    // 发送请求
+    pub async fn request(&mut self,
+                         method: 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();
+        let mut passphrase = "".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();
+        }
+        if self.login_param.contains_key("pass_key") {
+            passphrase = self.login_param.get("pass_key").unwrap().to_string();
+        }
+        let mut is_login_param = true;
+        if access_key == "" || secret_key == "" || passphrase == "" {
+            is_login_param = false
+        }
+
+        //请求头配置-如果需要登陆则存在额外配置
+        let mut body = "".to_string();
+        let timestamp = chrono::Utc::now().timestamp_millis().to_string();
+        let mut headers = HeaderMap::new();
+        headers.insert("Content-Type", "application/json".parse().unwrap());
+        headers.insert("locale", "en-US".parse().unwrap());
+        if method == "POST" {
+            body = params.clone();
+        }
+
+        //是否需要登陆-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {
+                //需要登陆-且登陆参数齐全
+                // trace!("param:{}", params);
+                // trace!("body:{}", body);
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      method.clone(),
+                                      prefix_url.clone(),
+                                      request_url.clone(),
+                                      params.clone(),
+                                      body.clone(),
+                                      timestamp.clone(),
+                );
+                //组装header
+                headers.extend(Self::headers(sing, timestamp, passphrase, access_key));
+            }
+        }
+
+
+        // trace!("headers:{:?}", headers);
+        let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let get_response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.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();
+        let res_data = Self::res_data_analysis(get_response, base_url, params);
+        res_data
+    }
+
+    pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
+        match result {
+            Ok(res_data) => {
+                if res_data.code != 200 {
+                    let json_value = res_data.data;
+                    let error = ResponseData::new("".to_string(),
+                                                  res_data.code,
+                                                  format!("请求地址:{}, 请求参数:{}, 响应结果:{}", base_url, params, res_data.message),
+                                                  json_value);
+                    error
+                } else {
+                    let json_value = res_data.data;
+
+
+                    let code = json_value["code"].as_str().unwrap();
+                    if code == "00000" {
+                        let data = serde_json::to_string(&json_value["data"]).unwrap();
+                        let success = ResponseData::new("".to_string(), 200, "success".to_string(), data.parse().unwrap());
+                        success
+                    } else {
+                        let code_num = i16::from_str(json_value["code"].as_str().unwrap()).unwrap();
+                        let error = ResponseData::new("".to_string(), code_num,
+                                                      format!("请求地址:{}, 请求参数:{}, 响应结果:{}", base_url, params, json_value.as_str().unwrap()),
+                                                      json_value);
+                        error
+                    }
+                }
+            }
+            Err(err) => {
+                let error = ResponseData::error("".to_string(), format!("json 解析失败:{}", err));
+                error
+            }
+        }
+    }
+
+    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();
+    }
+
+    // 封装请求头
+    pub fn headers(sign: String, timestamp: String, passphrase: String, access_key: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("ACCESS-KEY", access_key.parse().unwrap());
+        headers.insert("ACCESS-SIGN", sign.parse().unwrap());
+        headers.insert("ACCESS-TIMESTAMP", timestamp.parse().unwrap());
+        headers.insert("ACCESS-PASSPHRASE", passphrase.parse().unwrap());
+        headers
+    }
+
+
+    async fn http_tool(&mut 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: String = if RestTool::parse_params_to_str(params.clone()) == "" {
+            url.clone()
+        } else {
+            format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()))
+        };
+        // trace!("url:{}", url);
+        // trace!("addrs_url:{}", addrs_url);
+        // let params_json: serde_json::Value = serde_json::from_str(&params).unwrap();
+        // 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(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(self.label.clone(), 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(self.label.clone(), 200, "success".to_string(), body.parse().unwrap());
+        } else {
+            let body = response.text().await?;
+            res_data = ResponseData::error(self.label.clone(), body.to_string())
+        }
+
+        Ok(res_data)
+    }
+
+    // 对请求进行签名处理
+    pub fn sign(secret_key: String,
+            method: String, prefix_url: String, request_url: String,
+            params: String, body: String, timestamp: String) -> String {
+        let url_param_str = RestTool::parse_params_to_str(params);
+        let base_url = if method == "GET" && url_param_str.len() > 0 {
+            format!("{}{}?{}", prefix_url, request_url, url_param_str)
+        } else {
+            format!("{}{}", prefix_url, request_url)
+        };
+
+        // 时间戳 + 请求类型+ 请求参数字符串
+        let message = format!("{}{}{}{}", timestamp, method, base_url, body);
+        // trace!("message:{}",message);
+
+        // 做签名
+        let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+        let result = hmac::sign(&hmac_key, &message.as_bytes());
+        let sign = base64::encode(result);
+        sign
+    }
+}

+ 310 - 0
exchanges/src/bitget_swap_ws.rs

@@ -0,0 +1,310 @@
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use chrono::{Utc};
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::{json, Value};
+use tracing::{error, info, trace};
+use ring::hmac;
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+pub enum BitgetSwapWsType {
+    Public,
+    Private,
+}
+
+#[derive(Clone)]
+pub enum BitgetSwapSubscribeType {
+    PuTrade,
+    PuBooks1,
+
+    PrAccount,
+    PrPosition,
+    PrOrders,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BitgetSwapLogin {
+    pub api_key: String,
+    pub secret_key: String,
+    pub passphrase_key: String,
+}
+
+#[derive(Clone)]
+pub struct BitgetSwapWs {
+    label: String,                                              // 类型
+    address_url: String,                                        // 地址
+    login_param: Option<BitgetSwapLogin>,                       // 账号
+    symbol_s: Vec<String>,                                      // 币对
+    subscribe_types: Vec<BitgetSwapSubscribeType>,              // 订阅
+    heartbeat_time: u64,                                        // 心跳间隔
+}
+
+impl BitgetSwapWs {
+    pub fn new(is_colo: bool, login_param: Option<BitgetSwapLogin>, ws_type: BitgetSwapWsType) -> BitgetSwapWs {
+        return BitgetSwapWs::new_label("default-BitgetSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<BitgetSwapLogin>, ws_type: BitgetSwapWsType) -> BitgetSwapWs {
+        let address_url = match ws_type {
+            BitgetSwapWsType::Public => {
+                "wss://ws.bitget.com/v2/ws/public".to_string()
+            }
+            BitgetSwapWsType::Private => {
+                "wss://ws.bitget.com/v2/ws/private".to_string()
+            }
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        BitgetSwapWs {
+            label,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 10
+        }
+    }
+
+    // 添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BitgetSwapSubscribeType>) {
+        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("-", "");
+            *symbol = symbol.replace("_", "");
+        }
+        self.symbol_s = b_array;
+    }
+
+    //频道是否需要登录
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                BitgetSwapSubscribeType::PuTrade => false,
+                BitgetSwapSubscribeType::PuBooks1 => false,
+
+                BitgetSwapSubscribeType::PrAccount => true,
+                BitgetSwapSubscribeType::PrOrders => true,
+                BitgetSwapSubscribeType::PrPosition => true
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数*******************************************************/
+    /*******************************************************************************************************/
+    // 枚举解析成json
+    pub fn enum_to_json(symbol: String, subscribe_type: BitgetSwapSubscribeType) -> Value {
+        match subscribe_type {
+            // 公共订阅
+            BitgetSwapSubscribeType::PuTrade => {
+                json!({
+                    "instType": "USDT-FUTURES",
+                    "channel": "trade",
+                    "instId": symbol,
+                })
+            },
+            BitgetSwapSubscribeType::PuBooks1 => {
+                json!({
+                    "instType": "USDT-FUTURES",
+                    "channel": "books1",
+                    "instId": symbol,
+                })
+            },
+
+            // 私有订阅
+            BitgetSwapSubscribeType::PrAccount => {
+                json!({
+                    "instType": "USDT-FUTURES",
+                    "channel": "account",
+                    "coin": "default",
+                })
+            },
+            BitgetSwapSubscribeType::PrPosition => {
+                json!({
+                    "instType": "USDT-FUTURES",
+                    "channel": "positions",
+                    "instId": "default"
+                })
+            },
+            BitgetSwapSubscribeType::PrOrders => {
+                json!({
+                    "instType": "USDT-FUTURES",
+                    "channel": "orders",
+                    "instId": "default"
+                })
+            },
+        }
+    }
+
+    // 订阅信息生成
+    pub fn get_subscription(&self) -> String {
+        let mut params = vec![];
+        for symbol in &self.symbol_s {
+            for subscribe_type in &self.subscribe_types {
+                let ty_str = Self::enum_to_json(symbol.clone(), subscribe_type.clone());
+                params.push(ty_str);
+            }
+        }
+        let str = json!({
+            "op": "subscribe",
+            "args": params
+        });
+
+        str.to_string()
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 address_url = self.address_url.clone();
+        let label = self.label.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
+
+        // 设置订阅
+        let subscription = self.get_subscription();
+        let subscribe_array = vec![subscription.to_string()];
+        info!(?subscribe_array);
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        tokio::spawn(async move {
+            let ping_str = json!("ping");
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Custom(ping_str.as_str().unwrap().to_string()), heartbeat_time).await;
+        });
+
+        //链接
+        let login_param = self.login_param.clone();
+        let write_tx_clone2 = Arc::clone(write_tx_am);
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            loop {
+                info!("bitget_usdt_swap socket 连接中……");
+
+                // 登录相关
+                if login_is {
+                    let login_param_c = login_param.clone().unwrap();
+                    let timestamp = Utc::now().timestamp().to_string();
+                    // 时间戳 + 请求类型+ 请求参数字符串
+                    let message = format!("{}GET{}", timestamp, "/user/verify");
+                    let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, login_param_c.secret_key.as_bytes());
+                    let result = hmac::sign(&hmac_key, &message.as_bytes());
+                    let sign = base64::encode(result);
+
+                    let login_json = json!({
+                        "op": "login",
+                        "args": [{
+                            "apiKey": login_param_c.api_key,
+                            "passphrase": login_param_c.passphrase_key,
+                            "timestamp": timestamp,
+                            "sign": sign
+                        }]
+                    });
+                    let login_str = login_json.to_string();
+                    info!("发起ws登录: {}", login_str);
+                    let write_tx_c = Arc::clone(&write_tx_clone2);
+                    AbstractWsMode::send_subscribe(write_tx_c, Message::Text(login_str)).await;
+                }
+
+                // ws层重连
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 login_is, label.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong).await;
+
+                error!("bitget_usdt_swap socket 断连,重连中……");
+            }
+        });
+        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 ok_text(text: String) -> ResponseData {
+        let mut res_data = ResponseData::new("".to_string(), 200, text.clone(), Value::Null);
+        match text.as_str() {
+            "pong" => {
+                res_data.code = -301;
+                res_data.channel = "pong".to_string();
+                res_data.message = "success".to_string();
+            },
+            _ => {
+                let json_value: Value = serde_json::from_str(&text).unwrap();
+
+                if json_value.get("event").is_some() && json_value["event"].as_str() == Some("login") {
+                    if json_value.get("code").is_some() && json_value["code"] == 0 {
+                        res_data.message = "登陆成功".to_string();
+                    } else {
+                        res_data.message = format!("登陆失败:({},{})", json_value.get("code").as_ref().unwrap(), json_value.get("msg").as_ref().unwrap());
+                    }
+                    res_data.channel = "login".to_string();
+                    res_data.code = -200;
+                    res_data.data = json_value;
+                } else if json_value.get("event").is_some() && json_value["event"].as_str() == Some("subscribe") {
+                    res_data.code = -201;
+                    res_data.data = json_value.clone();
+                    res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+                    res_data.message = "success".to_string();
+                } else if json_value.get("action").is_some() {
+                    res_data.data = json_value["data"].clone();
+                    if res_data.data == "[]" {
+                        res_data.code = -1;
+                    } else {
+                        res_data.code = 200;
+                    }
+                    res_data.message = "success".to_string();
+                    res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+                    res_data.reach_time = json_value["ts"].as_i64().unwrap() * 1000;
+                }
+            }
+        }
+
+        res_data
+    }
+}

+ 481 - 0
exchanges/src/bybit_swap_rest.rs

@@ -0,0 +1,481 @@
+use std::collections::BTreeMap;
+
+use reqwest::Client;
+use reqwest::header::HeaderMap;
+use ring::hmac;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use tracing::{info, trace};
+
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+
+#[derive(Clone, Debug)]
+pub struct BybitSwapRest {
+    pub label: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl BybitSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BybitSwapRest
+    {
+        return BybitSwapRest::new_label("default-BybitSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BybitSwapRest {
+        let base_url = if is_colo {
+            "https://api.bytick.com".to_string()
+        } else {
+            "https://api.bytick.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        BybitSwapRest {
+            label,
+            base_url,
+            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(),
+                                "/v5".to_string(),
+                                "/market/time".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查詢最新行情信息
+    pub async fn get_tickers(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+               "category":"linear",
+                "symbol":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/market/tickers".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查詢市場價格K線數據
+    pub async fn get_kline(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+               "category":"linear",
+                "symbol":symbol,
+                "interval":"1",
+                "limit":"200"
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/market/kline".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查詢公告
+    pub async fn get_announcements(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "locale":"zh-TW"
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/announcements/index".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查詢可交易產品的規格信息
+    pub async fn get_instruments_info(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "category":"linear",
+            "symbol":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/market/instruments-info".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查看持仓信息
+    pub async fn get_positions(&mut self, symbol: String, settle_coin: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "category":"linear",
+         });
+        if symbol.len() > 0 {
+            params.as_object_mut().unwrap().insert("symbol".parse().unwrap(), serde_json::Value::from(symbol));
+        }
+        if settle_coin.len() > 0 {
+            params.as_object_mut().unwrap().insert("settleCoin".parse().unwrap(), serde_json::Value::from(settle_coin));
+        }
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/position/list".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //设置持仓模式
+    pub async fn set_position_mode(&mut self, symbol: String, mode: i64) -> ResponseData {
+        let params = serde_json::json!({
+             "category": "linear",
+             "symbol": symbol,
+             "mode": mode,
+         });
+        let data = self.request("POST".to_string(),
+                                "/v5".to_string(),
+                                "/position/switch-mode".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //設置槓桿
+    pub async fn set_leverage(&mut self, symbol: String,
+                              lever: String) -> ResponseData {
+        let params = serde_json::json!({
+             "category": "linear",
+             "symbol": symbol,
+             "buyLeverage": lever,
+             "sellLeverage": lever,
+         });
+        let data = self.request("POST".to_string(),
+                                "/v5".to_string(),
+                                "/position/set-leverage".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    //查詢錢包餘額
+    pub async fn get_account_balance(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "accountType":"UNIFIED",
+            "coin":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/account/wallet-balance".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //創建委託單
+    pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/v5".to_string(),
+                                "/order/create".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查詢實時委託單
+    pub async fn get_order(&mut self, symbol: String, order_id: String, order_link_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "category":"linear",
+            "symbol":symbol,
+         });
+        if order_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+        }
+        if order_link_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("orderLinkId".parse().unwrap(), serde_json::Value::from(order_link_id));
+        }
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/order/realtime".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //撤单
+    pub async fn cancel_order(&mut self, symbol: String, order_id: String, order_link_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+             "category": "linear",
+             "symbol": symbol,
+         });
+        if order_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("orderId".parse().unwrap(), serde_json::Value::from(order_id));
+        }
+        if order_link_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("orderLinkId".parse().unwrap(), serde_json::Value::from(order_link_id));
+        }
+        let data = self.request("POST".to_string(),
+                                "/v5".to_string(),
+                                "/order/cancel".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //撤銷所有訂單
+    pub async fn cancel_orders(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+             "category": "linear",
+             "symbol": symbol,
+         });
+        let data = self.request("POST".to_string(),
+                                "/v5".to_string(),
+                                "/order/cancel-all".to_string(),
+                                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();
+    }
+    //调用请求
+    pub async fn request(&mut self,
+                         method: 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();
+        // let mut passphrase = "".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();
+        }
+        // if self.login_param.contains_key("pass_key") {
+        //     passphrase = self.login_param.get("pass_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 = Self::get_timestamp();
+        let mut headers = HeaderMap::new();
+        headers.insert("Content-Type", "application/json; charset=utf-8".parse().unwrap());
+        headers.insert("X-BAPI-RECV-WINDOW", "5000".parse().unwrap());
+
+        if method == "POST" {
+            body = params.clone();
+        }
+
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {
+                //需要登录-且登录参数齐全
+                trace!("param:{}", params);
+                trace!("body:{}", body);
+                //组装sing
+                let sing = Self::sign(
+                    access_key.clone(),
+                    secret_key.clone(),
+                    method.clone(),
+                    params.clone(),
+                    timestamp.clone(),
+                );
+                //组装header
+                headers.extend(Self::headers(sing, timestamp, access_key));
+            }
+        }
+
+
+        // trace!("headers:{:?}", headers);
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response_data = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.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_data
+    }
+
+    pub fn headers(sign: String, timestamp: String, access_key: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("X-BAPI-SIGN-TYPE", "2".parse().unwrap());
+        headers.insert("X-BAPI-API-KEY", access_key.parse().unwrap());
+        headers.insert("X-BAPI-TIMESTAMP", timestamp.parse().unwrap());
+        headers.insert("X-BAPI-SIGN", sign.parse().unwrap());
+        // headers.insert("X-Referer", passphrase.parse().unwrap());
+        headers
+    }
+    pub fn sign(access_key: String,
+                secret_key: String,
+                method: String,
+                params: String, timestamp: String) -> String
+    {
+        /*签名生成*/
+        let url_param_str = RestTool::parse_params_to_str(params.clone());
+        let parameters = if method == "GET" {
+            url_param_str
+        } else {
+            params
+        };
+
+        let message = format!("{}{}5000{}", timestamp, access_key, parameters);
+        trace!("message:{}",message);
+
+        // 做签名
+        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 = hex::encode(result.as_ref());
+        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: String = if RestTool::parse_params_to_str(params.clone()) == "" {
+            url.clone()
+        } else {
+            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(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();
+        return if response.status().is_success() {
+            // 读取响应的内容
+            let body = response.text().await.unwrap();
+
+            self.on_success_data(&body, &addrs_url, &params)
+        } else {
+            let body = response.text().await.unwrap();
+
+            ResponseData::error(self.label.clone(), body)
+        }
+    }
+
+    pub fn on_success_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value: Value = serde_json::from_str(text.as_str()).unwrap();
+
+        let code: i64 = if json_value["retCode"].as_i64().is_some() {
+            json_value["retCode"].as_i64().unwrap()
+        } else if json_value["ret_code"].as_i64().is_some() {
+            json_value["ret_code"].as_i64().unwrap()
+        } else {
+            -1
+        };
+
+        if code == 0 {
+            let data = serde_json::to_string(&json_value["result"]).unwrap();
+            let mut success = ResponseData::new("".to_string(), 200, "success".to_string(), data.parse().unwrap());
+            success.time = json_value["time"].as_i64().unwrap();
+            success
+        } else {
+            let msg: &str = if json_value["retMsg"].as_str().is_some() {
+                json_value["retMsg"].as_str().unwrap()
+            } else if json_value["ret_msg"].as_str().is_some() {
+                json_value["ret_msg"].as_str().unwrap()
+            } else {
+                ""
+            };
+
+            let error = ResponseData::new("".to_string(),
+                                          code as i16,
+                                          format!("请求地址:{}, 请求参数:{}, 消息原文:{}。", base_url, params, msg),
+                                          Value::Null);
+            error
+        }
+    }
+
+    fn get_timestamp() -> String {
+        chrono::Utc::now().timestamp_millis()
+            .to_string()
+    }
+}

+ 347 - 0
exchanges/src/bybit_swap_ws.rs

@@ -0,0 +1,347 @@
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use chrono::Utc;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::{json, Value};
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use ring::hmac;
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+//类型
+pub enum BybitSwapWsType {
+    Public,
+    Private,
+}
+
+//订阅频道
+#[derive(Clone)]
+pub enum BybitSwapSubscribeType {
+    PuOrderBook1,
+    PuOrderBook50,
+    PuBlicTrade,
+    PuTickers,
+    PuKline(String),
+
+    PrPosition,
+    PrExecution,
+    PrOrder,
+    PrWallet,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BybitSwapLogin {
+    pub api_key: String,
+    pub secret_key: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BybitSwapWs {
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<BybitSwapLogin>,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<BybitSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl BybitSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<BybitSwapLogin>, ws_type: BybitSwapWsType) -> BybitSwapWs {
+        return BybitSwapWs::new_label("default-BybitSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<BybitSwapLogin>, ws_type: BybitSwapWsType) -> BybitSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            BybitSwapWsType::Public => {
+                "wss://stream.bybit.com/v5/public/linear?max_alive_time=1m".to_string()
+            }
+            BybitSwapWsType::Private => {
+                "wss://stream.bybit.com/v5/private?max_alive_time=1m".to_string()
+            }
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        BybitSwapWs {
+            label,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 10,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BybitSwapSubscribeType>) {
+        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("_", "");
+            *symbol = symbol.replace("-", "");
+        }
+        self.symbol_s = b_array;
+    }
+    //频道是否需要登录
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                BybitSwapSubscribeType::PuOrderBook1 => false,
+                BybitSwapSubscribeType::PuOrderBook50 => false,
+                BybitSwapSubscribeType::PuBlicTrade => false,
+                BybitSwapSubscribeType::PuTickers => false,
+                BybitSwapSubscribeType::PuKline(_) => false,
+
+                BybitSwapSubscribeType::PrPosition => true,
+                BybitSwapSubscribeType::PrExecution => true,
+                BybitSwapSubscribeType::PrOrder => true,
+                BybitSwapSubscribeType::PrWallet => true,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: BybitSwapSubscribeType) -> String {
+        match subscribe_type {
+            BybitSwapSubscribeType::PuOrderBook1 => {
+                format!("orderbook.1.{}", symbol)
+            }
+            BybitSwapSubscribeType::PuOrderBook50 => {
+                format!("orderbook.50.{}", symbol)
+            }
+            BybitSwapSubscribeType::PuBlicTrade => {
+                format!("publicTrade.{}", symbol)
+            }
+            BybitSwapSubscribeType::PuTickers => {
+                format!("tickers.{}", symbol)
+            }
+            BybitSwapSubscribeType::PuKline(t) => {
+                format!("kline.{}.{}", t, symbol)
+            }
+
+            BybitSwapSubscribeType::PrPosition => {
+                format!("position")
+            }
+            BybitSwapSubscribeType::PrExecution => {
+                format!("execution")
+            }
+            BybitSwapSubscribeType::PrOrder => {
+                format!("order")
+            }
+            BybitSwapSubscribeType::PrWallet => {
+                format!("wallet")
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> String {
+        let mut params = 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());
+                params.push(ty_str);
+            }
+        }
+
+        let str = json!({
+                "op": "subscribe",
+                "args": params
+            });
+        str.to_string()
+    }
+    /*******************************************************************************************************/
+    /*****************************************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 label = self.label.clone();
+        let login_param = self.login_param.clone();
+        let (api_key, secret_key) = match login_param {
+            None => { ("".to_string(), "".to_string()) }
+            Some(p) => {
+                (p.api_key.clone().to_string(), p.secret_key.clone().to_string())
+            }
+        };
+        let heartbeat_time = self.heartbeat_time.clone();
+        trace!("{:?}",format!("{}",subscription));
+
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time).await;
+            trace!("线程-异步心跳-结束");
+        });
+
+        //链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            loop {
+                info!("bybit_usdt_swap socket 连接中……");
+
+                //设置订阅
+                let mut subscribe_array = vec![];
+                if login_is {
+                    let timestamp = Utc::now().timestamp_millis();
+                    let expires = timestamp + 1000;
+                    let message = format!("GET/realtime{}", expires);
+
+                    let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+                    let result = hmac::sign(&hmac_key, &message.as_bytes());
+                    let signature = hex::encode(result.as_ref());
+
+                    //登录相关
+                    let str = json!({
+                        "op": "auth",
+                        "args": [api_key, expires, signature]
+                    });
+                    subscribe_array.push(str.to_string());
+                }
+                subscribe_array.push(subscription.to_string());
+
+                // ws网络层重连
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, label.clone(), subscribe_array, write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong).await;
+
+                error!("bybit_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 ok_text(text: String) -> ResponseData {
+        // trace!("原始数据");
+        // 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.get("success").is_some() {
+            info!("bybit_swap_ws订阅结果:{:?}", json_value);
+            //订阅内容
+            let success = json_value["success"].as_bool().unwrap();
+            // let ret_msg = json_value["ret_msg"].as_str().unwrap();
+            let op = json_value["op"].as_str().unwrap();
+            let success_error = if success {
+                "成功"
+            } else {
+                "失败"
+            };
+
+            if op == "auth" {
+                res_data.code = -200;
+                res_data.message = format!("登录{}", success_error);
+            } else if op == "subscribe" {
+                res_data.message = format!("订阅{}", success_error);
+                res_data.code = -201;
+            } else {
+                res_data.code = -1;
+                res_data.channel = "未知订阅".to_string();
+            }
+        } else if json_value.get("topic").is_some() && json_value.get("data").is_some() {
+            let channel = json_value["topic"].to_string();
+            res_data.data = json_value["data"].clone();
+
+            res_data.code = 200;
+
+            if channel.contains("orderbook") {
+                res_data.channel = "orderbook".to_string();
+                res_data.data_type = json_value["type"].as_str().unwrap().to_string();
+                // bybit 时间在data块外
+                res_data.reach_time = json_value.get("ts").unwrap().as_i64().unwrap_or(0i64);
+            } else if channel.contains("publicTrade") {
+                res_data.channel = "trade".to_string();
+                res_data.data_type = json_value["type"].as_str().unwrap().to_string();
+            } else if channel.contains("tickers") {
+                res_data.channel = "tickers".to_string();
+                res_data.data["ts"] = json_value["ts"].clone();
+            } else if channel.contains("kline") {
+                res_data.channel = "kline".to_string();
+            } else if channel.contains("position") {
+                res_data.channel = "position".to_string();
+            } else if channel.contains("execution") {
+                res_data.channel = "execution".to_string();
+            } else if channel.contains("order") {
+                res_data.channel = "order".to_string();
+            } else if channel.contains("wallet") {
+                res_data.channel = "wallet".to_string();
+            } else {
+                res_data.code = -1;
+                res_data.channel = "未知的频道".to_string();
+            }
+        } else {
+            //推送数据
+            res_data.code = -1;
+            res_data.channel = "未知的频道".to_string();
+        }
+
+        res_data
+    }
+}

+ 285 - 0
exchanges/src/crypto_spot_ws.rs

@@ -0,0 +1,285 @@
+// use std::sync::Arc;
+// use std::sync::atomic::AtomicBool;
+//
+// use chrono::Utc;
+// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+// use serde_json::json;
+// use tokio::sync::Mutex;
+// use tokio_tungstenite::tungstenite::{Error, Message};
+// use tracing::{info, trace};
+//
+// use crate::response_base::ResponseData;
+// use crate::socket_tool::AbstractWsMode;
+//
+// //类型
+// pub enum CryptoSpotWsType {
+//     Public,
+//     Private,
+// }
+//
+// //订阅频道
+// #[derive(Clone)]
+// pub enum CryptoSpotSubscribeType {
+//     PuBook,
+//     PuTicker,
+//     PuTrade,
+//     PuCandlestick,
+// }
+//
+// //账号信息
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct CryptoSpotLogin {
+//     pub api_key: String,
+//     pub api_secret: String,
+// }
+//
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct CryptoSpotWs {
+//     //类型
+//     label: String,
+//     //地址
+//     address_url: String,
+//     //账号
+//     login_param: Option<CryptoSpotLogin>,
+//     //币对
+//     symbol_s: Vec<String>,
+//     //订阅
+//     subscribe_types: Vec<CryptoSpotSubscribeType>,
+//     //心跳间隔
+//     heartbeat_time: u64,
+// }
+//
+// impl CryptoSpotWs {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//     pub fn new(is_colo: bool, login_param: Option<CryptoSpotLogin>, ws_type: CryptoSpotWsType) -> CryptoSpotWs {
+//         return CryptoSpotWs::new_label("default-CryptoSpotWs".to_string(), is_colo, login_param, ws_type);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: Option<CryptoSpotLogin>, ws_type: CryptoSpotWsType) -> CryptoSpotWs {
+//         /*******公共频道-私有频道数据组装*/
+//         let address_url = match ws_type {
+//             CryptoSpotWsType::Public => {
+//                 "wss://stream.crypto.com/exchange/v1/market".to_string()
+//             }
+//             CryptoSpotWsType::Private => {
+//                 "wss://stream.crypto.com/exchange/v1/user".to_string()
+//             }
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",address_url);
+//         } else {
+//             info!("走普通通道:{}",address_url);
+//         }
+//
+//         CryptoSpotWs {
+//             label,
+//             address_url,
+//             login_param,
+//             symbol_s: vec![],
+//             subscribe_types: vec![],
+//             heartbeat_time: 1000 * 3,
+//         }
+//     }
+//
+//     /*******************************************************************************************************/
+//     /*****************************************订阅函数********************************************************/
+//     /*******************************************************************************************************/
+//     //手动添加订阅信息
+//     pub fn set_subscribe(&mut self, subscribe_types: Vec<CryptoSpotSubscribeType>) {
+//         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("_", "");
+//             *symbol = symbol.replace("-", "");
+//         }
+//         self.symbol_s = b_array;
+//     }
+//     //频道是否需要登录
+//     fn contains_pr(&self) -> bool {
+//         for t in self.subscribe_types.clone() {
+//             if match t {
+//                 CryptoSpotSubscribeType::PuBook => false,
+//                 CryptoSpotSubscribeType::PuTicker => false,
+//                 CryptoSpotSubscribeType::PuTrade => false,
+//                 CryptoSpotSubscribeType::PuCandlestick => false,
+//             } {
+//                 return true;
+//             }
+//         }
+//         false
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************工具函数********************************************************/
+//     /*******************************************************************************************************/
+//     //订阅枚举解析
+//     pub fn enum_to_string(symbol: String, subscribe_type: CryptoSpotSubscribeType) -> String {
+//         match subscribe_type {
+//             CryptoSpotSubscribeType::PuBook => {
+//                 format!("book.{}-PERP", symbol)
+//             }
+//             CryptoSpotSubscribeType::PuTicker => {
+//                 format!("ticker.{}-PERP", symbol)
+//             }
+//             CryptoSpotSubscribeType::PuTrade => {
+//                 format!("trade.{}-PERP", symbol)
+//             }
+//             CryptoSpotSubscribeType::PuCandlestick => {
+//                 format!("candlestick.M1.{}-PERP", symbol)
+//             }
+//         }
+//     }
+//     //订阅信息生成
+//     pub fn get_subscription(&self) -> String {
+//         let mut params = 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());
+//                 params.push(ty_str);
+//             }
+//         }
+//
+//         let nonce = Utc::now().timestamp_millis();
+//         let str = json!({
+//                   "id": 1,
+//                   "method": "subscribe",
+//                   "params": {
+//                     "channels":params
+//                   },
+//                   "nonce": nonce
+//                 });
+//
+//         if params.len() > 0 {
+//             str.to_string()
+//         } else {
+//             "".to_string()
+//         }
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************socket基本*****************************************************/
+//     /*******************************************************************************************************/
+//     //链接
+//     pub async fn ws_connect_async(&mut self,
+//                                   is_shutdown_arc: Arc<AtomicBool>,
+//                                   _write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+//                                   write_rx: UnboundedReceiver<Message>,
+//                                   read_tx: UnboundedSender<ResponseData>) -> Result<(), Error>
+//     {
+//         let login_is = self.contains_pr();
+//         let subscription = self.get_subscription();
+//         let address_url = self.address_url.clone();
+//         let label = self.label.clone();
+//         // let heartbeat_time = self.heartbeat_time.clone();
+//
+//
+//         //心跳-- 方法内部线程启动
+//         // let write_tx_clone1 = Arc::clone(write_tx_am);
+//         // tokio::spawn(async move {
+//         //     trace!("线程-异步心跳-开始");
+//         //     AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Pong, heartbeat_time).await;
+//         //     trace!("线程-异步心跳-结束");
+//         // });
+//
+//         //设置订阅
+//         let mut subscribe_array = vec![];
+//         if login_is {
+//             //登录相关
+//         }
+//         subscribe_array.push(subscription.to_string());
+//
+//         //链接
+//         let t2 = tokio::spawn(async move {
+//             trace!("线程-异步链接-开始");
+//             match AbstractWsMode::ws_connect_async(is_shutdown_arc, address_url.clone(),
+//                                                    label.clone(), subscribe_array,
+//                                                    write_rx, read_tx,
+//                                                    Self::message_text,
+//                                                    Self::message_ping,
+//                                                    Self::message_pong,
+//             ).await {
+//                 Ok(_) => { trace!("线程-异步链接-结束"); }
+//                 Err(e) => { trace!("发生异常:crypt-期货链接关闭-{:?}",e); }
+//             }
+//         });
+//         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".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析-pong
+//     pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+//         return Option::from(ResponseData::new("".to_string(), "-301".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析
+//     pub fn ok_text(text: String) -> ResponseData {
+//         // trace!("原始数据");
+//         // trace!(?text);
+//
+//         let mut res_data = ResponseData::new("".to_string(), "".to_string(), "success".to_string(), "".to_string());
+//         let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+//
+//         if json_value.get("id").is_some() && json_value.get("method").is_some() && json_value.get("code").is_some() {
+//             let id = json_value["id"].as_i64().unwrap();
+//             let method = json_value["method"].as_str().unwrap();
+//             let code = json_value["code"].as_i64().unwrap();
+//
+//             if method == "public/heartbeat" {
+//                 if code == 0 {
+//                     res_data.code = "-302".to_string();
+//                     let str = json!({
+//                   "id": id,
+//                   "method": "public/respond-heartbeat"
+//                 });
+//                     res_data.message = "服务器主动心跳检测,客户端回应~!".to_string();
+//                     res_data.data = str.to_string();
+//                 } else {
+//                     res_data.message = "心跳异常~!".to_string();
+//                 }
+//             } else if method == "subscribe" && json_value.get("channel").is_some() {
+//                 //订阅反馈
+//                 if code == 0 {
+//                     res_data.code = "-201".to_string();
+//                     res_data.channel = json_value["channel"].as_str().unwrap().to_string();
+//                 } else {
+//                     res_data.message = "订阅失败~!".to_string();
+//                     res_data.data = json_value["data"].to_string()
+//                 }
+//             } else if method == "subscribe" && json_value.get("result").is_some() {
+//                 if code == 0 {
+//                     let subscription = json_value["result"]["subscription"].as_str().unwrap();
+//                     res_data.channel = subscription.to_string();
+//                     res_data.code = "200".to_string();
+//                     res_data.data = json_value["result"]["data"].to_string()
+//                 } else {
+//                     res_data.message = "推送数据异常~!".to_string();
+//                     res_data.data = json_value["result"]["data"].to_string()
+//                 }
+//             } else {
+//                 res_data.message = "未知解析!!".to_string();
+//             }
+//         } else {
+//             res_data.message = "错误解析!!".to_string();
+//         }
+//         res_data
+//     }
+// }

+ 15 - 0
exchanges/src/gate_spot_rest.rs

@@ -0,0 +1,15 @@
+use std::collections::BTreeMap;
+
+#[derive(Clone)]
+pub struct GateSpotRest {}
+
+impl GateSpotRest {
+    pub fn new(_is_colo: bool, _login_param: BTreeMap<String, String>) -> GateSpotRest
+    {
+        return GateSpotRest::new_label("default-GateSpotRest".to_string(), _is_colo, _login_param);
+    }
+    pub fn new_label(_label: String, _is_colo: bool, _login_param: BTreeMap<String, String>) -> GateSpotRest
+    {
+        GateSpotRest {}
+    }
+}

+ 15 - 0
exchanges/src/gate_spot_ws.rs

@@ -0,0 +1,15 @@
+// use std::collections::BTreeMap;
+//
+// #[derive(Clone)]
+// pub struct GateSpotWs {}
+//
+// impl GateSpotWs {
+//     pub fn new(_is_colo: bool, _login_param: BTreeMap<String, String>) -> GateSpotWs
+//     {
+//         return GateSpotWs::new_label("default-GateSpotWs".to_string(), _is_colo, _login_param);
+//     }
+//     pub fn new_label(_label: String, _is_colo: bool, _login_param: BTreeMap<String, String>) -> GateSpotWs
+//     {
+//         GateSpotWs {}
+//     }
+// }

+ 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 {
+    label: 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_label("default-GateSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: 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 {
+            label,
+            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"),
+                                true,
+                                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),
+                                true,
+                                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),
+                                true,
+                                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.label.clone(), format!("未知下单方向!{}", side));
+        }
+        if pos_side != "long" && pos_side != "short" {
+            ResponseData::error(self.label.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.label.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.label.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["label"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["label"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.label.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
+            }
+        }
+    }
+}

+ 359 - 0
exchanges/src/gate_swap_ws.rs

@@ -0,0 +1,359 @@
+use std::str::FromStr;
+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 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 {
+    //类型
+    label: 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_label("default-GateSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_label(label: 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 {
+            label,
+            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 label = self.label.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, label.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong).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 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 = i16::from_str(json_value["error"]["code"].as_str().unwrap()).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
+            }
+        }
+    }
+}

+ 521 - 0
exchanges/src/kucoin_spot_rest.rs

@@ -0,0 +1,521 @@
+// use std::collections::BTreeMap;
+// use std::fmt::Debug;
+// use reqwest::header::HeaderMap;
+// use hmac::{Hmac, Mac, NewMac};
+// use reqwest::{Client};
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::FromPrimitive;
+// use rust_decimal_macros::dec;
+// use sha2::Sha256;
+// use tracing::{info, trace};
+// use crate::http_tool::RestTool;
+// use crate::response_base::ResponseData;
+// use std::string::String;
+//
+//
+// #[derive(Clone, Debug)]
+// pub struct KucoinSpotRest {
+//     pub label: String,
+//     base_url: String,
+//     client: reqwest::Client,
+//     /*******参数*/
+//     //是否需要登录
+//     //登录所需参数
+//     login_param: BTreeMap<String, String>,
+//     delays: Vec<i64>,
+//     max_delay: i64,
+//     avg_delay: Decimal,
+//
+// }
+//
+// impl KucoinSpotRest {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//
+//     pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> KucoinSpotRest
+//     {
+//         return KucoinSpotRest::new_label("default-KucoinSpotRest".to_string(), is_colo, login_param);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> KucoinSpotRest {
+//         let base_url = if is_colo {
+//             "https://api.kucoin.com".to_string()
+//         } else {
+//             "https://api.kucoin.com".to_string()
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",base_url);
+//         } else {
+//             info!("走普通通道:{}",base_url);
+//         }
+//         /*****返回结构体*******/
+//         KucoinSpotRest {
+//             label,
+//             base_url,
+//             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/v1".to_string(),
+//                                 "/timestamp".to_string(),
+//                                 false,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     // 获取成交记录
+//     pub async fn get_fills(&mut self,
+//                            symbol: String,
+//                            order_id: String,
+//                            side: String,
+//                            start_at: i64,
+//                            end_at: i64,
+//                            page_size: i64,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "symbol":symbol,
+//             "pageSize": 1000
+//          });
+//         if order_id.len() > 0 {
+//             params["orderId"] = serde_json::json!(order_id);
+//         }
+//         if side.len() > 0 {
+//             params["side"] = serde_json::json!(side);
+//         }
+//         if start_at > 0 {
+//             params["startAt"] = serde_json::json!(start_at);
+//         }
+//         if end_at > 0 {
+//             params["endAt"] = serde_json::json!(end_at);
+//         }
+//         if page_size > 0 {
+//             params["pageSize"] = serde_json::json!(page_size);
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 "/fills".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取订单
+//     pub async fn get_order(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//              "status":"active",
+//              "tradeType":"TRADE",
+//              "type":"limit"
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 "/orders".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //獲取行情
+//     pub async fn get_level1(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//              "symbol":symbol
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 "/market/orderbook/level1".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //通過orderId获取訂單詳情
+//     pub async fn get_order_by_order_id(&mut self, order_id: String) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/orders/{}", order_id),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //通過clientOid獲取訂單詳情
+//     pub async fn get_order_by_client_id(&mut self, client_id: String) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/order/client-order/{}", client_id),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //獲取賬戶列表 - 現貨/槓桿/現貨高頻
+//     pub async fn get_accounts(&mut self, currency: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "type":"trade",
+//          });
+//         if currency.len() > 0 {
+//             params.as_object_mut().unwrap().insert("currency".parse().unwrap(), serde_json::Value::from(currency));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/accounts"),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //獲取交易對列表
+//     pub async fn get_symbols(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v2".to_string(),
+//                                 format!("/symbols"),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //通過orderId撤單
+//     pub async fn cancel_order_by_order_id(&mut self, order_id: String) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("DELETE".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/orders/{}", order_id),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //通過clientOid撤單
+//     pub async fn cancel_order_by_client_id(&mut self, client_id: String) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("DELETE".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/order/client-order/{}", client_id),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //全部撤單
+//     pub async fn cancel_order_all(&mut self, symbol: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "tradeType":"TRADE"
+//          });
+//         if symbol.len() > 0 {
+//             params.as_object_mut().unwrap().insert("symbol".parse().unwrap(), serde_json::Value::from(symbol));
+//         }
+//         let data = self.request("DELETE".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/orders"),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //现货下单-
+//     pub async fn spot_order(&mut self, params: serde_json::Value) -> ResponseData {
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 format!("/orders"),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //获取合约令牌-公共
+//     pub async fn get_public_token(&mut self) -> ResponseData {
+//         let params = serde_json::json!({});
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 "/bullet-public".to_string(),
+//                                 false,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取合约令牌-私有
+//     pub async fn get_private_token(&mut self) -> ResponseData {
+//         let params = serde_json::json!({});
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v1".to_string(),
+//                                 "/bullet-private".to_string(),
+//                                 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();
+//     }
+//     //调用请求
+//     pub async fn request(&mut self,
+//                          method: 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();
+//         let mut pass_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();
+//         }
+//         if self.login_param.contains_key("pass_key") {
+//             pass_key = self.login_param.get("pass_key").unwrap().to_string();
+//         }
+//         let mut is_login_param = true;
+//         if access_key == "" || secret_key == "" || pass_key == "" {
+//             is_login_param = false
+//         }
+//
+//
+//         //请求头配置-如果需要登录则存在额外配置
+//         let mut body = "".to_string();
+//
+//         let timestamp = chrono::Utc::now().timestamp_millis().to_string();
+//
+//         let mut headers = HeaderMap::new();
+//         headers.insert("Content-Type", "application/json".parse().unwrap());
+//         if method == "POST" {
+//             body = params.clone();
+//         }
+//
+//         //是否需要登录-- 组装sing
+//         if is_login {
+//             if !is_login_param {
+//                 let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+//                 return e;
+//             } else {
+//                 //需要登录-且登录参数齐全
+//                 trace!("param:{}", params);
+//                 trace!("body:{}", body);
+//                 //组装sing
+//                 let sing = Self::sign(secret_key.clone(),
+//                                       method.clone(),
+//                                       prefix_url.clone(),
+//                                       request_url.clone(),
+//                                       params.clone(),
+//                                       body.clone(),
+//                                       timestamp.clone(),
+//                 );
+//                 trace!("sing:{}", sing);
+//                 let passphrase = Self::passphrase(secret_key, pass_key);
+//                 trace!("passphrase:{}", passphrase);
+//                 //组装header
+//                 headers.extend(Self::headers(sing, timestamp, passphrase, access_key));
+//             }
+//         }
+//
+//
+//         trace!("headers:{:?}", headers);
+//         let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+//         let start_time = chrono::Utc::now().timestamp_millis();
+//         let get_response = self.http_tool(
+//             base_url.clone(),
+//             method.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();
+//         let res_data = Self::res_data_analysis(get_response, base_url, params);
+//         res_data
+//     }
+//
+//     pub fn headers(sign: String, timestamp: String, passphrase: String, access_key: String) -> HeaderMap {
+//         let mut headers = HeaderMap::new();
+//         headers.insert("KC-API-KEY", access_key.parse().unwrap());
+//         headers.insert("KC-API-SIGN", sign.parse().unwrap());
+//         headers.insert("KC-API-TIMESTAMP", timestamp.parse().unwrap());
+//         headers.insert("KC-API-PASSPHRASE", passphrase.parse().unwrap());
+//         headers.insert("KC-API-KEY-VERSION", "2".parse().unwrap());
+//         headers
+//     }
+//     pub fn sign(secret_key: String,
+//                 method: 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.clone());
+//         trace!("body_data:{}", body_data);
+//         // let body = Some(body_data);
+//         // let hashed_payload = if let Some(body) = body {
+//         //     let mut m = digest::Context::new(&digest::SHA256);
+//         //     m.update(body.as_bytes());
+//         //     hex::encode(m.finish().as_ref())
+//         // } else {
+//         //     String::new()
+//         // };
+//         // trace!("hashed_payload:{}", hashed_payload);
+//
+//         let mut message = format!("{}{}{}",
+//                                   timestamp,
+//                                   method,
+//                                   url
+//         );
+//         if method == "GET" || method == "DELETE" {
+//             message = if params_str.len() > 0 {
+//                 format!("{}?{}", message, params_str)
+//             } else {
+//                 format!("{}", message)
+//             };
+//         } else if method == "POST" || method == "PUT" {
+//             message = format!("{}{}", message, body_data);
+//         }
+//
+//         trace!("**********", );
+//         trace!("组装数据:{}", message);
+//         trace!("**********", );
+//
+//         let mut mac = Hmac::<Sha256>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+//         mac.update(message.as_bytes());
+//         let result = mac.finalize().into_bytes();
+//         let base64_encoded = base64::encode(result);
+//         base64_encoded
+//     }
+//
+//     pub fn passphrase(secret_key: String, pass_key: String) -> String
+//     {
+//         let mut mac = Hmac::<Sha256>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+//         mac.update(pass_key.as_bytes());
+//         let result = mac.finalize().into_bytes();
+//         let base64_encoded = base64::encode(result);
+//         base64_encoded
+//     }
+//
+//
+//     async fn http_tool(&mut 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: String = if RestTool::parse_params_to_str(params.clone()) == "" {
+//             url.clone()
+//         } else {
+//             format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()))
+//         };
+//         trace!("url:{}", url);
+//         trace!("addrs_url:{}", addrs_url);
+//
+//
+//         let req = match request_type.as_str() {
+//             "GET" => self.client.get(addrs_url.clone()).headers(headers),
+//             "POST" => self.client.post(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(self.label.clone(), 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(self.label.clone(), "200".to_string(), "success".to_string(), body);
+//         } else {
+//             let body = response.text().await?;
+//             // trace!("error-----{}", body);
+//             res_data = ResponseData::error(self.label.clone(), body.to_string())
+//         }
+//
+//         Ok(res_data)
+//     }
+//
+//
+//     //res_data 解析
+//     pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
+//         match result {
+//             Ok(res_data) => {
+//                 if res_data.code != "200" {
+//                     // res_data
+//                     let mut error = res_data;
+//                     error.message = format!("错误:{},url:{},相关参数:{}", error.message, base_url, params);
+//                     error
+//                 } else {
+//                     let body: String = res_data.data;
+//                     let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+//
+//                     let code = json_value["code"].as_str().unwrap();
+//
+//                     if code != "200000" {
+//                         let msg = json_value["msg"].as_str().unwrap();
+//                         // let error = ResponseData::new("".to_string(), code.to_string(),
+//                         //                               format!("错误:{},url:{},相关参数:{}", msg, base_url, params),
+//                         //                               "".parse().unwrap());
+//                         // error
+//
+//                         let mut error = ResponseData::error(res_data.label, msg.parse().unwrap());
+//                         error.code = code.parse().unwrap();
+//                         error.data = format!("请求地址:{},请求参数:{}", base_url, params);
+//                         error
+//                     } else {
+//                         let data = serde_json::to_string(&json_value["data"]).unwrap();
+//                         let success = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), data.parse().unwrap());
+//                         success
+//                     }
+//                 }
+//             }
+//             Err(err) => {
+//                 let error = ResponseData::error("".to_string(), format!("json 解析失败:{},相关参数:{}", err, params));
+//                 error
+//             }
+//         }
+//     }
+// }

+ 378 - 0
exchanges/src/kucoin_spot_ws.rs

@@ -0,0 +1,378 @@
+// use std::collections::BTreeMap;
+// use std::sync::Arc;
+// use std::sync::atomic::AtomicBool;
+//
+// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+// use tokio::sync::Mutex;
+// use tokio_tungstenite::tungstenite::{Error, Message};
+// use tracing::{error, info, trace};
+//
+// use crate::kucoin_spot_rest::KucoinSpotRest;
+// use crate::response_base::ResponseData;
+// use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+//
+// //类型
+// pub enum KucoinSpotWsType {
+//     Public,
+//     Private,
+// }
+//
+// #[derive(Debug)]
+// #[derive(Clone)]
+// pub struct KucoinSpotWsParam {
+//     pub token: String,
+//     pub ws_url: String,
+//     pub ws_ping_interval: i64,
+//     pub ws_ping_timeout: i64,
+//     pub is_ok_subscribe: bool,
+// }
+//
+// //订阅频道
+// #[derive(Clone)]
+// pub enum KucoinSpotSubscribeType {
+//     PuSpotMarketLevel2Depth50,
+//     PuMarketMatch,
+//     PuMarketTicker,
+//
+//     PrSpotMarketTradeOrders,
+//     PrAccountBalance,
+// }
+//
+// //账号信息
+// #[derive(Clone, Debug)]
+// pub struct KucoinSpotLogin {
+//     pub access_key: String,
+//     pub secret_key: String,
+//     pub pass_key: String,
+// }
+//
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct KucoinSpotWs {
+//     //类型
+//     label: String,
+//     //地址
+//     address_url: String,
+//     //代理信息
+//     login_param: Option<KucoinSpotLogin>,
+//     //登录数据
+//     ws_param: KucoinSpotWsParam,
+//     //币对
+//     symbol_s: Vec<String>,
+//     //订阅
+//     subscribe_types: Vec<KucoinSpotSubscribeType>,
+//     //心跳间隔
+//     heartbeat_time: u64,
+// }
+//
+// impl KucoinSpotWs {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//     pub async fn new(is_colo: bool, login_param: Option<KucoinSpotLogin>, ws_type: KucoinSpotWsType) -> KucoinSpotWs {
+//         return KucoinSpotWs::new_label("default-KucoinSpotWs".to_string(), is_colo, login_param, ws_type).await;
+//     }
+//     pub async fn new_label(label: String, is_colo: bool, login_param: Option<KucoinSpotLogin>, ws_type: KucoinSpotWsType) -> KucoinSpotWs {
+//         /*******公共频道-私有频道数据组装*/
+//         let mut ws_param = KucoinSpotWsParam {
+//             token: "".to_string(),
+//             ws_url: "".to_string(),
+//             ws_ping_interval: 0,
+//             ws_ping_timeout: 0,
+//             is_ok_subscribe: false,
+//         };
+//
+//         /*******公共频道-私有频道数据组装*/
+//         let res_data = KucoinSpotWs::get_rul_token(ws_type, login_param.clone()).await;
+//         let address_url = match res_data {
+//             Ok(param) => {
+//                 ws_param = param;
+//                 format!("{}?token={}", ws_param.ws_url, ws_param.token)
+//             }
+//             Err(error) => {
+//                 error!("-链接地址等参数错误:{:?}", error);
+//                 "".to_string()
+//             }
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",address_url);
+//         } else {
+//             info!("走普通通道:{}",address_url);
+//         }
+//
+//         KucoinSpotWs {
+//             label,
+//             address_url,
+//             login_param,
+//             ws_param,
+//             symbol_s: vec![],
+//             subscribe_types: vec![],
+//             heartbeat_time: 1000 * 18,
+//         }
+//     }
+//
+//     /*******************************************************************************************************/
+//     /*****************************************订阅函数********************************************************/
+//     /*******************************************************************************************************/
+//     //根据当前类型获取对应的频道 地址 与 token
+//     async fn get_rul_token(ws_type: KucoinSpotWsType, login_param: Option<KucoinSpotLogin>) -> Result<KucoinSpotWsParam, reqwest::Error> {
+//         let mut kucoin_exc = KucoinSpotRest::new(false, match login_param {
+//             None => {
+//                 let btree_map: BTreeMap<String, String> = BTreeMap::new();
+//                 btree_map
+//             }
+//             Some(d) => {
+//                 let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+//                 btree_map.insert("access_key".to_string(), d.access_key);
+//                 btree_map.insert("secret_key".to_string(), d.secret_key);
+//                 btree_map.insert("pass_key".to_string(), d.pass_key);
+//                 btree_map
+//             }
+//         });
+//         let res_data = match ws_type {
+//             KucoinSpotWsType::Public => {
+//                 kucoin_exc.get_public_token().await
+//             }
+//             KucoinSpotWsType::Private => {
+//                 kucoin_exc.get_private_token().await
+//             }
+//         };
+//
+//         trace!("kucoin-spot-rest 获取ws连接地址:{:?}",res_data);
+//
+//         if res_data.code == "200" {
+//             let mut ws_url = "".to_string();
+//             let mut ws_token = "".to_string();
+//             let mut ws_ping_interval: i64 = 0;
+//             let mut ws_ping_timeout: i64 = 0;
+//
+//
+//             //数据解析
+//             let parsed_json: serde_json::Value = serde_json::from_str(res_data.data.as_str()).unwrap();
+//             if let Some(value) = parsed_json.get("token") {
+//                 let formatted_value = match value {
+//                     serde_json::Value::String(s) => s.clone(),
+//                     _ => value.to_string()
+//                 };
+//                 ws_token = format!("{}", formatted_value);
+//             }
+//             if let Some(endpoint) = parsed_json["instanceServers"][0]["endpoint"].as_str() {
+//                 ws_url = format!("{}", endpoint);
+//             }
+//             if let Some(ping_interval) = parsed_json["instanceServers"][0]["pingInterval"].as_i64() {
+//                 ws_ping_interval = ping_interval;
+//             }
+//             if let Some(ping_timeout) = parsed_json["instanceServers"][0]["pingTimeout"].as_i64() {
+//                 ws_ping_timeout = ping_timeout;
+//             }
+//
+//
+//             Ok(KucoinSpotWsParam { ws_url, token: ws_token, ws_ping_interval, ws_ping_timeout, is_ok_subscribe: false })
+//         } else {
+//             error!("公共/私有-频道获取失败:{:?}", res_data);
+//             panic!("公共/私有-频道获取失败:{:?}", res_data);
+//         }
+//     }
+//     //手动添加订阅信息
+//     pub fn set_subscribe(&mut self, subscribe_types: Vec<KucoinSpotSubscribeType>) {
+//         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 {
+//                 KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50 => false,
+//                 KucoinSpotSubscribeType::PuMarketMatch => false,
+//                 KucoinSpotSubscribeType::PuMarketTicker => false,
+//
+//                 KucoinSpotSubscribeType::PrSpotMarketTradeOrders => true,
+//                 KucoinSpotSubscribeType::PrAccountBalance => true
+//             } {
+//                 return true;
+//             }
+//         }
+//         false
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************工具函数********************************************************/
+//     /*******************************************************************************************************/
+//     //订阅枚举解析
+//     pub fn enum_to_string(symbol: String, subscribe_type: KucoinSpotSubscribeType) -> serde_json::Value {
+//         match subscribe_type {
+//             KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50 => {//level2
+//                 serde_json::json!({
+//                      "topic": format!("/spotMarket/level2Depth50:{}", symbol),
+//                      "type": "subscribe",
+//                      "response": true
+//                 })
+//             }
+//             KucoinSpotSubscribeType::PuMarketMatch => {//match
+//                 serde_json::json!({
+//                      "topic": format!("/market/match:{}", symbol),
+//                      "type": "subscribe",
+//                      "response": true
+//                 })
+//             }
+//             KucoinSpotSubscribeType::PuMarketTicker => {//ticker
+//                 serde_json::json!({
+//                      "topic": format!("/market/ticker:{}", symbol),
+//                      "type": "subscribe",
+//                      "response": true
+//                 })
+//             }
+//
+//             KucoinSpotSubscribeType::PrSpotMarketTradeOrders => {//market.tradeOrders
+//                 serde_json::json!({
+//                     "type": "subscribe",
+//                     "topic": "/spotMarket/tradeOrders",
+//                     "privateChannel":true,
+//                     "response":true,
+//                 })
+//             }
+//             KucoinSpotSubscribeType::PrAccountBalance => {//account.balance
+//                 serde_json::json!({
+//                     "type": "subscribe",
+//                     "topic": "/account/balance",
+//                     "privateChannel":true,
+//                     "response":true,
+//                 })
+//             }
+//         }
+//     }
+//     //订阅信息生成
+//     pub fn get_subscription(&self) -> Vec<String> {
+//         let mut array = 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());
+//                 array.push(ty_str.to_string());
+//             }
+//         }
+//         array
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************socket基本*****************************************************/
+//     /*******************************************************************************************************/
+//     //链接
+//     pub async fn ws_connect_async(&mut self,
+//                                   is_shutdown_arc: Arc<AtomicBool>,
+//                                   write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+//                                   write_rx: UnboundedReceiver<Message>,
+//                                   read_tx: UnboundedSender<ResponseData>,
+//     ) -> Result<(), Error>
+//     {
+//         let login_is = self.contains_pr();
+//         let subscription = self.get_subscription();
+//         let address_url = self.address_url.clone();
+//         let label = self.label.clone();
+//         let heartbeat_time = self.ws_param.ws_ping_interval;
+//
+//         //心跳-- 方法内部线程启动
+//         let write_tx_clone1 = write_tx_am.clone();
+//         tokio::spawn(async move {
+//             trace!("线程-异步心跳-开始");
+//             AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time as u64).await;
+//             trace!("线程-异步心跳-结束");
+//         });
+//
+//
+//         //设置订阅
+//         let subscribe_array = subscription.clone();
+//         if login_is {
+//             //登录相关
+//         }
+//         // let write_tx_clone2 = write_tx_am.clone();
+//         // tokio::spawn(async move {
+//         //     tokio::time::sleep(Duration::from_millis(3 * 1000)).await;
+//         //     for su in subscription.clone(){
+//         //         let write_tx_clone = write_tx_clone2.lock().await;
+//         //         let message = Message::Text(su.clone());
+//         //         write_tx_clone.unbounded_send(message).unwrap();
+//         //     }
+//         // });
+//
+//
+//         //1 链接
+//
+//         let t2 = tokio::spawn(async move {
+//             trace!("线程-异步链接-开始");
+//             match AbstractWsMode::ws_connect_async(is_shutdown_arc, address_url.clone(),
+//                                                    label.clone(), subscribe_array.clone(),
+//                                                    write_rx, read_tx,
+//                                                    Self::message_text,
+//                                                    Self::message_ping,
+//                                                    Self::message_pong,
+//             ).await {
+//                 Ok(_) => { trace!("线程-异步链接-结束"); }
+//                 Err(e) => { trace!("发生异常:kucoin-现货链接关闭-{:?}",e); }
+//             }
+//         });
+//         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".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析-pong
+//     pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+//         return Option::from(ResponseData::new("".to_string(), "-301".to_string(), "success".to_string(), "".to_string()));
+//     }
+//
+//     //数据解析
+//     pub fn ok_text(text: String) -> ResponseData
+//     {
+//         let mut res_data = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), "".to_string());
+//         let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+//         //订阅 相应
+//         if json_value["type"].as_str() == Option::from("welcome") {
+//             //链接成功
+//             res_data.code = "-200".to_string();
+//             res_data.message = "链接成功,主动发起订阅".to_string();
+//             trace!("链接成功,主动发起订阅:");
+//         } else if json_value["type"].as_str() == Option::from("ack") {
+//             res_data.code = "-201".to_string();
+//             res_data.message = "订阅成功".to_string();
+//         } else if json_value["type"].as_str() == Option::from("error") {
+//             res_data.code = format!("{}", json_value["code"]);
+//             res_data.message = format!("{}", json_value["data"].as_str().unwrap());
+//         } else if json_value.get("topic").is_some() {
+//             res_data.channel = format!("{}", json_value["subject"].as_str().unwrap());
+//
+//             if json_value["topic"].as_str() == Option::from("/contractAccount/wallet") {
+//                 res_data.code = "".to_string();
+//                 if json_value["subject"].as_str() == Option::from("availableBalance.change") {
+//                     res_data.code = "200".to_string();
+//                     res_data.data = json_value["data"].to_string();
+//                 } else {}
+//             } else {
+//                 res_data.data = json_value["data"].to_string();
+//             }
+//         } else {
+//             res_data.code = "".to_string();
+//             res_data.message = "未知解析".to_string();
+//         }
+//         res_data
+//     }
+// }

+ 625 - 0
exchanges/src/kucoin_swap_rest.rs

@@ -0,0 +1,625 @@
+use std::collections::BTreeMap;
+use reqwest::header::HeaderMap;
+use hmac::{Hmac, Mac, NewMac};
+use reqwest::{Client};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use sha2::Sha256;
+use tracing::{info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+
+#[derive(Clone, Debug)]
+pub struct KucoinSwapRest {
+    pub label: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl KucoinSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> KucoinSwapRest
+    {
+        return KucoinSwapRest::new_label("default-KucoinSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> KucoinSwapRest {
+        let base_url = if is_colo {
+            "https://api-futures.kucoin.com".to_string()
+        } else {
+            "https://api-futures.kucoin.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        KucoinSwapRest {
+            label,
+            base_url,
+            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/v1".to_string(),
+                                "/timestamp".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    // 获取成交记录
+    pub async fn get_fills(&mut self,
+                           symbol: String,
+                           order_id: String,
+                           side: String,
+                           start_at: i64,
+                           end_at: i64,
+                           page_size: i64,
+    ) -> ResponseData {
+        let mut params = serde_json::json!({
+            "symbol":symbol,
+            "pageSize":1000
+         });
+        if order_id.len() > 0 {
+            params["orderId"] = serde_json::json!(order_id);
+        }
+        if side.len() > 0 {
+            params["side"] = serde_json::json!(side);
+        }
+        if start_at > 0 {
+            params["startAt"] = serde_json::json!(start_at);
+        }
+        if end_at > 0 {
+            params["endAt"] = serde_json::json!(end_at);
+        }
+        if page_size > 0 {
+            params["pageSize"] = serde_json::json!(page_size);
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                "/fills".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询合约账户
+    pub async fn get_account(&mut self, contract: String) -> ResponseData {
+        let params = serde_json::json!({
+            "currency":contract
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                "/account-overview".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取仓位信息
+    pub async fn get_position(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                "/position".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询所有的合约信息
+    pub async fn get_market_details(&mut self) -> ResponseData {
+        let params = serde_json::json!({});
+
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/contracts/active"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //实时行情
+    pub async fn get_ticker(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/ticker"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查看订单列表
+    pub async fn get_orders(&mut self, status: String, symbol: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            // "symbol":symbol
+         });
+        if symbol.len() > 0 {
+            params.as_object_mut().unwrap().insert("symbol".parse().unwrap(), serde_json::Value::from(symbol));
+        }
+        if status.len() > 0 {
+            params.as_object_mut().unwrap().insert("status".parse().unwrap(), serde_json::Value::from(status));
+        }
+        trace!("??{}",params.to_string());
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/orders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取用户仓位列表
+    pub async fn get_positions(&mut self, currency: String) -> ResponseData {
+        let params = serde_json::json!({
+            "currency":currency
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/positions"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //单个订单详情
+    pub async fn get_orders_details(&mut self, order_id: String, client_id: String) -> ResponseData {
+        let mut params = serde_json::json!({   });
+        let mut url = String::from("");
+        if order_id != "" {
+            url = format!("/orders/{}", order_id);
+        } else if client_id != "" {
+            url = format!("/orders/byClientOid");
+            params.as_object_mut().unwrap().insert("clientOid".parse().unwrap(), serde_json::Value::from(client_id));
+        }
+
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                url,
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/orders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //下单
+    pub async fn swap_bazaar_order(&mut self,
+                                   client_oid: String,
+                                   symbol: String,
+                                   origin_side: String,
+                                   size: u64,
+                                   leverage: String,
+                                   price: String,
+                                   order_type: String) -> ResponseData
+    {
+        let mut side = String::from("");
+        let mut params = serde_json::json!({
+            "clientOid":client_oid,
+            "symbol": symbol,
+            "size":size,
+            "leverage":leverage,
+            "reduceOnly":false,
+            "price":price,
+            "type":order_type,
+        });
+
+        let req = match origin_side.as_str() {
+            "kd" => {
+                side = "buy".to_string();
+                true
+            }
+            "pd" => {
+                side = "sell".to_string();
+                true
+            }
+            "kk" => {
+                side = "sell".to_string();
+                true
+            }
+            "pk" => {
+                side = "buy".to_string();
+                true
+            }
+            _ => { false } // 处理未知请求类型
+        };
+        if req {
+            params.as_object_mut().unwrap().insert("side".to_string(), serde_json::json!(side));
+        }
+
+        let data = self.swap_order(params).await;
+        data
+    }
+
+    //单个撤单
+    pub async fn cancel_order(&mut self, order_id: String, client_id: String) -> ResponseData {
+        let mut params = serde_json::json!({   });
+        let mut url = String::from("");
+        if order_id != "" {
+            url = format!("/orders/{}", order_id);
+        } else if client_id != "" {
+            url = format!("/orders/byClientOid");
+            params.as_object_mut().unwrap().insert("clientOid".parse().unwrap(), serde_json::Value::from(client_id));
+        }
+        let data = self.request("DELETE".to_string(),
+                                "/api/v1".to_string(),
+                                url,
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //批量撤单
+    pub async fn cancel_orders(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({   });
+        let data = self.request("DELETE".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/orders?symbol={}", symbol),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //全部撤單
+    pub async fn cancel_order_all(&mut self) -> ResponseData {
+        let params = serde_json::json!({   });
+        let data = self.request("DELETE".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/orders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    //获取合约令牌-公共
+    pub async fn get_public_token(&mut self) -> ResponseData {
+        let params = serde_json::json!({});
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                "/bullet-public".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取合约令牌-私有
+    pub async fn get_private_token(&mut self) -> ResponseData {
+        let params = serde_json::json!({});
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                "/bullet-private".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //设置杠杆(修改階梯風險限額等級)
+    pub async fn set_leverage(&mut self, symbol: String, level: i8) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "level":level,
+        });
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/position/risk-limit-level/change"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查看杠杆(查詢杠桿代幣信息)
+    pub async fn get_leverage(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+        });
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/contracts/risk-limit/{}", symbol),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //设置 自动追加保证金
+    pub async fn auto_deposit_status(&mut self, symbol: String, status: bool) -> ResponseData {
+        let params = serde_json::json!({
+                "symbol":symbol,
+                "status":status
+        });
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/position/margin/auto-deposit-status"),
+                                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();
+    }
+    //调用请求
+    pub async fn request(&mut self,
+                         method: 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();
+        let mut pass_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();
+        }
+        if self.login_param.contains_key("pass_key") {
+            pass_key = self.login_param.get("pass_key").unwrap().to_string();
+        }
+        let mut is_login_param = true;
+        if access_key == "" || secret_key == "" || pass_key == "" {
+            is_login_param = false
+        }
+
+
+        //请求头配置-如果需要登录则存在额外配置
+        let mut body = "".to_string();
+
+        let timestamp = chrono::Utc::now().timestamp_millis().to_string();
+
+        let mut headers = HeaderMap::new();
+        headers.insert("Content-Type", "application/json".parse().unwrap());
+        if method == "POST" {
+            body = params.clone();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {
+                //需要登录-且登录参数齐全
+                trace!("param:{}", params);
+                trace!("body:{}", body);
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      method.clone(),
+                                      prefix_url.clone(),
+                                      request_url.clone(),
+                                      params.clone(),
+                                      body.clone(),
+                                      timestamp.clone(),
+                );
+                trace!("sing:{}", sing);
+                let passphrase = Self::passphrase(secret_key, pass_key);
+                trace!("passphrase:{}", passphrase);
+                //组装header
+                headers.extend(Self::headers(sing, timestamp, passphrase, access_key));
+            }
+        }
+
+
+        // trace!("headers:{:?}", headers);
+        let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let get_response = self.http_tool(
+            base_url.clone(),
+            method.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();
+        let res_data = Self::res_data_analysis(get_response, base_url, params);
+        res_data
+    }
+
+    pub fn headers(sign: String, timestamp: String, passphrase: String, access_key: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("KC-API-KEY", access_key.parse().unwrap());
+        headers.insert("KC-API-SIGN", sign.parse().unwrap());
+        headers.insert("KC-API-TIMESTAMP", timestamp.parse().unwrap());
+        headers.insert("KC-API-PASSPHRASE", passphrase.parse().unwrap());
+        headers.insert("KC-API-KEY-VERSION", "2".parse().unwrap());
+        headers
+    }
+    pub fn sign(secret_key: String,
+                method: 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.clone());
+        trace!("body_data:{}", body_data);
+        // let body = Some(body_data);
+        // let hashed_payload = if let Some(body) = body {
+        //     let mut m = digest::Context::new(&digest::SHA256);
+        //     m.update(body.as_bytes());
+        //     hex::encode(m.finish().as_ref())
+        // } else {
+        //     String::new()
+        // };
+        // trace!("hashed_payload:{}", hashed_payload);
+
+        let mut message = format!("{}{}{}",
+                                  timestamp,
+                                  method,
+                                  url
+        );
+        if method == "GET" || method == "DELETE" {
+            message = if params_str.len() > 0 {
+                format!("{}?{}", message, params_str)
+            } else {
+                format!("{}", message)
+            };
+        } else if method == "POST" || method == "PUT" {
+            message = format!("{}{}", message, body_data);
+        }
+
+        trace!("**********", );
+        trace!("组装数据:{}", message);
+        trace!("**********", );
+
+        let mut mac = Hmac::<Sha256>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+        mac.update(message.as_bytes());
+        let result = mac.finalize().into_bytes();
+        let base64_encoded = base64::encode(result);
+        base64_encoded
+    }
+
+    pub fn passphrase(secret_key: String, pass_key: String) -> String
+    {
+        let mut mac = Hmac::<Sha256>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+        mac.update(pass_key.as_bytes());
+        let result = mac.finalize().into_bytes();
+        let base64_encoded = base64::encode(result);
+        base64_encoded
+    }
+
+
+    async fn http_tool(&mut 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: String = if RestTool::parse_params_to_str(params.clone()) == "" {
+            url.clone()
+        } else {
+            format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()))
+        };
+        trace!("url:{}", url);
+        trace!("addrs_url:{}", addrs_url);
+
+        let req = match request_type.as_str() {
+            "GET" => self.client.get(addrs_url.clone()).headers(headers),
+            "POST" => self.client.post(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(self.label.clone(), format!("错误的请求类型:{}", request_type.clone()))), // 处理未知请求类型
+        };
+
+        let response = req.send().await?;
+        if response.status().is_success() {
+            // 读取响应的内容
+            let body = response.text().await?;
+
+            let data = serde_json::from_str(body.as_str()).unwrap();
+            res_data = ResponseData::new(self.label.clone(), 200, "success".to_string(), data);
+        } else {
+            let body = response.text().await?;
+            // trace!("error-----{}", body);
+            res_data = ResponseData::error(self.label.clone(), body.to_string())
+        }
+
+        Ok(res_data)
+    }
+
+
+    //res_data 解析
+    pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
+        match result {
+            Ok(res_data) => {
+                if res_data.code != 200 {
+                    // res_data
+                    let mut error = res_data;
+                    error.message = format!("错误:{},url:{},相关参数:{}", error.message, base_url, params);
+                    error
+                } else {
+                    let json_value = res_data.data;
+
+                    let code = json_value["code"].as_str().unwrap();
+
+                    if code != "200000" {
+                        let msg = json_value["msg"].as_str().unwrap();
+                        // let error = ResponseData::new("".to_string(), code.to_string(),
+                        //                               format!("错误:{},url:{},相关参数:{}", msg, base_url, params),
+                        //                               "".parse().unwrap());
+                        // error
+
+                        let mut error = ResponseData::error(res_data.label, msg.parse().unwrap());
+                        error.code = code.parse().unwrap();
+                        error.message = format!("请求地址:{},请求参数:{},响应:{}。", base_url, params, json_value.to_string());
+                        error
+                    } else {
+                        let data = serde_json::to_string(&json_value["data"]).unwrap();
+                        let success = ResponseData::new("".to_string(), 200, "success".to_string(), data.parse().unwrap());
+                        success
+                    }
+                }
+            }
+            Err(err) => {
+                let error = ResponseData::error("".to_string(), format!("json 解析失败:{},相关参数:{}", err, params));
+                error
+            }
+        }
+    }
+}

+ 397 - 0
exchanges/src/kucoin_swap_ws.rs

@@ -0,0 +1,397 @@
+use std::collections::BTreeMap;
+use std::str::FromStr;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::Value;
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::kucoin_swap_rest::KucoinSwapRest;
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+//类型
+pub enum KucoinSwapWsType {
+    Public,
+    Private,
+}
+
+
+#[derive(Debug)]
+#[derive(Clone)]
+pub struct KucoinSwapWsParam {
+    pub token: String,
+    pub ws_url: String,
+    pub ws_ping_interval: i64,
+    pub ws_ping_timeout: i64,
+    pub is_ok_subscribe: bool,
+}
+
+//订阅频道
+#[derive(Clone)]
+pub enum KucoinSwapSubscribeType {
+    //买卖盘 快照,asks:卖,bids:买入
+    PuContractMarketLevel2Depth50,
+
+    PuContractMarketExecution,
+    PuContractMarkettickerV2,
+
+    PrContractAccountWallet,
+    PrContractPosition,
+    PrContractMarketTradeOrdersSys,
+    PrContractMarketTradeOrders,
+}
+
+//账号信息
+#[derive(Clone, Debug)]
+pub struct KucoinSwapLogin {
+    pub access_key: String,
+    pub secret_key: String,
+    pub pass_key: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct KucoinSwapWs {
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<KucoinSwapLogin>,
+    //登录数据
+    ws_param: KucoinSwapWsParam,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<KucoinSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl KucoinSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub async fn new(is_colo: bool, login_param: Option<KucoinSwapLogin>, ws_type: KucoinSwapWsType) -> KucoinSwapWs {
+        return Self::new_label("default-KucoinSwapWs".to_string(), is_colo, login_param, ws_type).await;
+    }
+    pub async fn new_label(label: String, is_colo: bool, login_param: Option<KucoinSwapLogin>, ws_type: KucoinSwapWsType) -> KucoinSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let mut ws_param = KucoinSwapWsParam {
+            token: "".to_string(),
+            ws_url: "".to_string(),
+            ws_ping_interval: 0,
+            ws_ping_timeout: 0,
+            is_ok_subscribe: false,
+        };
+
+        /*******公共频道-私有频道数据组装*/
+        let res_data = Self::get_rul_token(ws_type, login_param.clone()).await;
+        let address_url = match res_data {
+            Ok(param) => {
+                ws_param = param;
+                format!("{}?token={}", ws_param.ws_url, ws_param.token)
+            }
+            Err(error) => {
+                error!("-链接地址等参数错误:{:?}", error);
+                "".to_string()
+            }
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        KucoinSwapWs {
+            label,
+            address_url,
+            login_param,
+            ws_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 18,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //根据当前类型获取对应的频道 地址 与 token
+    async fn get_rul_token(ws_type: KucoinSwapWsType, login_param: Option<KucoinSwapLogin>) -> Result<KucoinSwapWsParam, reqwest::Error> {
+        let mut kucoin_exc = KucoinSwapRest::new(false, match login_param {
+            None => {
+                let btree_map: BTreeMap<String, String> = BTreeMap::new();
+                btree_map
+            }
+            Some(d) => {
+                let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+                btree_map.insert("access_key".to_string(), d.access_key);
+                btree_map.insert("secret_key".to_string(), d.secret_key);
+                btree_map.insert("pass_key".to_string(), d.pass_key);
+                btree_map
+            }
+        });
+
+
+        let res_data = match ws_type {
+            KucoinSwapWsType::Public => {
+                kucoin_exc.get_public_token().await
+            }
+            KucoinSwapWsType::Private => {
+                kucoin_exc.get_private_token().await
+            }
+        };
+
+        trace!("kucoin-swap-rest 获取ws连接地址:{:?}",res_data);
+
+        if res_data.code == 200 {
+            let mut ws_url = "".to_string();
+            let mut ws_token = "".to_string();
+            let mut ws_ping_interval: i64 = 0;
+            let mut ws_ping_timeout: i64 = 0;
+
+
+            //数据解析
+            let parsed_json = res_data.data;
+            if let Some(value) = parsed_json.get("token") {
+                let formatted_value = match value {
+                    serde_json::Value::String(s) => s.clone(),
+                    _ => value.to_string()
+                };
+                ws_token = format!("{}", formatted_value);
+            }
+            if let Some(endpoint) = parsed_json["instanceServers"][0]["endpoint"].as_str() {
+                ws_url = format!("{}", endpoint);
+            }
+            if let Some(ping_interval) = parsed_json["instanceServers"][0]["pingInterval"].as_i64() {
+                ws_ping_interval = ping_interval;
+            }
+            if let Some(ping_timeout) = parsed_json["instanceServers"][0]["pingTimeout"].as_i64() {
+                ws_ping_timeout = ping_timeout;
+            }
+
+
+            Ok(KucoinSwapWsParam { ws_url, token: ws_token, ws_ping_interval, ws_ping_timeout, is_ok_subscribe: false })
+        } else {
+            error!("公共/私有-频道获取失败:{:?}", res_data);
+            panic!("公共/私有-频道获取失败:{:?}", res_data);
+        }
+    }
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<KucoinSwapSubscribeType>) {
+        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("_", "");
+            *symbol = symbol.replace("-", "");
+        }
+        self.symbol_s = b_array;
+    }
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                KucoinSwapSubscribeType::PuContractMarketLevel2Depth50 => false,
+                KucoinSwapSubscribeType::PuContractMarketExecution => false,
+                KucoinSwapSubscribeType::PuContractMarkettickerV2 => false,
+
+                KucoinSwapSubscribeType::PrContractAccountWallet => true,
+                KucoinSwapSubscribeType::PrContractPosition => true,
+                KucoinSwapSubscribeType::PrContractMarketTradeOrdersSys => true,
+                KucoinSwapSubscribeType::PrContractMarketTradeOrders => true,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: KucoinSwapSubscribeType) -> serde_json::Value {
+        match subscribe_type {
+            KucoinSwapSubscribeType::PuContractMarketLevel2Depth50 => {//level2
+                serde_json::json!({
+                     "topic": format!("/contractMarket/level2Depth50:{}", symbol),
+                     "type": "subscribe",
+                     "response": true
+                })
+            }
+            KucoinSwapSubscribeType::PuContractMarketExecution => {//match
+                serde_json::json!({
+                     "topic": format!("/contractMarket/execution:{}", symbol),
+                     "type": "subscribe",
+                     "response": true
+                })
+            }
+            KucoinSwapSubscribeType::PuContractMarkettickerV2 => {//tickerV2
+                serde_json::json!({
+                     "topic": format!("/contractMarket/tickerV2:{}", symbol),
+                     "type": "subscribe",
+                     "response": true
+                })
+            }
+            KucoinSwapSubscribeType::PrContractAccountWallet => {//orderMargin.change
+                serde_json::json!({
+                    "type": "subscribe",
+                    "topic": "/contractAccount/wallet",
+                    "privateChannel":true,
+                    "response":true,
+                })
+            }
+            KucoinSwapSubscribeType::PrContractPosition => {//position.change
+                serde_json::json!({
+                    "type": "subscribe",
+                    "topic": format!("/contract/position:{}", symbol),
+                    "privateChannel":true,
+                    "response":true,
+                })
+            }
+            KucoinSwapSubscribeType::PrContractMarketTradeOrdersSys => {//orderChange
+                serde_json::json!({
+                    "type": "subscribe",
+                    "topic": format!("/contractMarket/tradeOrders"),
+                    "privateChannel":true,
+                    "response":true,
+                })
+            }
+            KucoinSwapSubscribeType::PrContractMarketTradeOrders => {//symbolOrderChange
+                serde_json::json!({
+                    "type": "subscribe",
+                    "topic": format!("/contractMarket/tradeOrders:{}", symbol),
+                    "privateChannel":true,
+                    "response":true,
+                })
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<String> {
+        let mut array = 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());
+                array.push(ty_str.to_string());
+            }
+        }
+        array
+    }
+    /*******************************************************************************************************/
+    /*****************************************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 label = self.label.clone();
+        let heartbeat_time = self.ws_param.ws_ping_interval.clone();
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = write_tx_am.clone();
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time as u64).await;
+            trace!("线程-异步心跳-结束");
+        });
+
+
+        //设置订阅
+        let subscribe_array = subscription.clone();
+        if login_is {
+            //登录相关
+        }
+
+        //1 链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            loop {
+                info!("kucoin_usdt_swap socket 连接中……");
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, label.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong).await;
+
+                error!("kucoin_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 ok_text(text: String) -> ResponseData
+    {
+        // trace!("原始数据:{:?}",text);
+        let mut res_data = ResponseData::new("".to_string(), 200, "success".to_string(), Value::Null);
+        let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+
+        //订阅 相应
+        if json_value["type"].as_str() == Option::from("welcome") {
+            //链接成功
+            res_data.code = -200;
+            res_data.message = "链接成功,主动发起订阅".to_string();
+        } else if json_value["type"].as_str() == Option::from("ack") {
+            res_data.code = -201;
+            res_data.message = "订阅成功".to_string();
+        } else if json_value["type"].as_str() == Option::from("error") {
+            res_data.code = i16::from_str(json_value["code"].as_str().unwrap()).unwrap();
+            res_data.message = format!("{}", json_value["data"].as_str().unwrap());
+        } else if json_value.get("topic").is_some() {
+            res_data.channel = format!("{}", json_value["subject"].as_str().unwrap());
+
+            if json_value["topic"].as_str() == Option::from("/contractAccount/wallet") {
+                res_data.code = -1;
+                if json_value["subject"].as_str() == Option::from("availableBalance.change") {
+                    res_data.code = 200;
+                    res_data.data = json_value["data"].clone();
+                } else {}
+            } else {
+                res_data.data = json_value["data"].clone();
+            }
+        } else {
+            res_data.code = -1;
+            res_data.message = "未知解析".to_string();
+        }
+        res_data
+    }
+}

+ 28 - 0
exchanges/src/lib.rs

@@ -0,0 +1,28 @@
+pub mod proxy;
+pub mod response_base;
+pub mod http_tool;
+pub mod binance_spot_rest;
+pub mod binance_spot_ws;
+pub mod gate_spot_rest;
+pub mod gate_spot_ws;
+pub mod gate_swap_ws;
+pub mod gate_swap_rest;
+pub mod socket_tool;
+pub mod kucoin_swap_rest;
+pub mod kucoin_swap_ws;
+pub mod okx_swap_ws;
+pub mod binance_swap_ws;
+pub mod okx_swap_rest;
+pub mod binance_swap_rest;
+mod utils;
+pub mod bitget_spot_ws;
+pub mod bitget_spot_rest;
+pub mod bitget_swap_ws;
+pub mod bitget_swap_rest;
+pub mod kucoin_spot_ws;
+pub mod kucoin_spot_rest;
+pub mod crypto_spot_ws;
+pub mod bybit_swap_rest;
+pub mod xlsx_utils;
+pub mod bybit_swap_ws;
+

+ 580 - 0
exchanges/src/okx_swap_rest.rs

@@ -0,0 +1,580 @@
+// use std::collections::BTreeMap;
+// use reqwest::header::HeaderMap;
+// use reqwest::{Client};
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::FromPrimitive;
+// use rust_decimal_macros::dec;
+// use tracing::{info, trace};
+// use crate::http_tool::RestTool;
+// use crate::response_base::ResponseData;
+// use ring::hmac;
+//
+// #[derive(Clone, Debug)]
+// pub struct OkxSwapRest {
+//     pub label: String,
+//     base_url: String,
+//     client: reqwest::Client,
+//     /*******参数*/
+//     //是否需要登录
+//     //登录所需参数
+//     login_param: BTreeMap<String, String>,
+//     delays: Vec<i64>,
+//     max_delay: i64,
+//     avg_delay: Decimal,
+//
+// }
+//
+// impl OkxSwapRest {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//
+//     pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> OkxSwapRest
+//     {
+//         return OkxSwapRest::new_label("default-OkxSwapRest".to_string(), is_colo, login_param);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> OkxSwapRest {
+//         let base_url = if is_colo {
+//             "https://www.okx.com".to_string()
+//         } else {
+//             "https://www.okx.com".to_string()
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",base_url);
+//         } else {
+//             info!("走普通通道:{}",base_url);
+//         }
+//         /*****返回结构体*******/
+//         OkxSwapRest {
+//             label,
+//             base_url,
+//             client: Client::new(),
+//             login_param,
+//             delays: vec![],
+//             max_delay: 0,
+//             avg_delay: dec!(0.0),
+//         }
+//     }
+//
+//     /*******************************************************************************************************/
+//     /*****************************************rest请求函数********************************************************/
+//     /*******************************************************************************************************/
+//     //获取订单信息
+//     pub async fn get_order(&mut self, symbol: String, ord_id: String, cl_ord_id: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "instId":symbol
+//          });
+//         if ord_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("ordId".parse().unwrap(), serde_json::Value::from(ord_id));
+//         }
+//         if cl_ord_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("clOrdId".parse().unwrap(), serde_json::Value::from(cl_ord_id));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/trade/order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取未成交订单列表
+//     pub async fn get_incomplete_order(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "instId":symbol
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/trade/orders-pending".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取系统时间
+//     pub async fn get_server_time(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/public/time".to_string(),
+//                                 false,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //查看持仓信息
+//     pub async fn get_positions(&mut self, inst_type: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//          });
+//         if inst_type.len() > 0 {
+//             params.as_object_mut().unwrap().insert("instType".parse().unwrap(), serde_json::Value::from(inst_type));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/account/positions".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取单个产品行情信息
+//     pub async fn get_ticker(&mut self, symbol: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "instId":symbol
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/market/ticker".to_string(),
+//                                 false,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //查看账户余额
+//     pub async fn get_balance(&mut self, ccy: String) -> ResponseData {
+//         let params = serde_json::json!({
+//             "ccy":ccy
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/account/balance".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取交易产品基础信息
+//     pub async fn get_instruments(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//            "instType":"SWAP"
+//          });
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/public/instruments".to_string(),
+//                                 false,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //获取成交明细(近三天)
+//     pub async fn get_trade_fills(&mut self, after: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//              "instType": "SWAP",
+//              "limit":"100"
+//          });
+//         if after.len() > 0 {
+//             params.as_object_mut().unwrap().insert("after".parse().unwrap(), serde_json::Value::from(after));
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/trade/fills".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //获取成交明细(近三个月)
+//     pub async fn get_trade_fills_history(&mut self, inst_id: String, begin: String, end: String, limit: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//             "instType": "SWAP",
+//             "instId":inst_id,
+//             "limit":100,
+//          });
+//
+//         if begin.len() > 0 {
+//             params["begin"] = serde_json::json!(begin);
+//         }
+//         if end.len() > 0 {
+//             params["end"] = serde_json::json!(end);
+//         }
+//         if limit.len() > 0 {
+//             params["limit"] = serde_json::json!(limit);
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/trade/fills-history".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //合约-下单
+//     pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/trade/order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //撤单
+//     pub async fn cancel_order(&mut self, symbol: String, ord_id: String, cl_ord_id: String) -> ResponseData {
+//         let mut params = serde_json::json!({
+//              "instId": symbol
+//          });
+//         if ord_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("ordId".parse().unwrap(), serde_json::Value::from(ord_id));
+//         }
+//         if cl_ord_id.len() > 0 {
+//             params.as_object_mut().unwrap().insert("clOrdId".parse().unwrap(), serde_json::Value::from(cl_ord_id));
+//         }
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/trade/cancel-order".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //设置杠杆倍数-单币种保证金`账户在`全仓`交易模式下,设置`币币杠杆`的杠杆倍数(币对层面)
+//     pub async fn set_leverage(&mut self, symbol: String, lever: String) -> ResponseData {
+//         let params = serde_json::json!({
+//              "instId": symbol,
+//              "lever": lever,
+//              "mgnMode": "cross"
+//          });
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/account/set-leverage".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//     //设置持仓模式
+//     pub async fn set_position_mode(&mut self) -> ResponseData {
+//         let params = serde_json::json!({
+//              "posMode": "long_short_mode",
+//          });
+//         let data = self.request("POST".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/account/set-position-mode".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //获取历史订单记录(近七天)
+//     pub async fn get_orders_history(&mut self,
+//                                     sprd_id: String,
+//                                     ord_type: String,
+//                                     state: String,
+//                                     begin: String,
+//                                     end: String,
+//                                     limit: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//          });
+//         if sprd_id.len() > 0 {
+//             params["sprdId"] = serde_json::json!(sprd_id);
+//         }
+//         if ord_type.len() > 0 {
+//             params["ordType"] = serde_json::json!(ord_type);
+//         }
+//         if state.len() > 0 {
+//             params["state"] = serde_json::json!(state);
+//         }
+//         if begin.len() > 0 {
+//             params["begin"] = serde_json::json!(begin);
+//         }
+//         if end.len() > 0 {
+//             params["end"] = serde_json::json!(end);
+//         }
+//         if limit.len() > 0 {
+//             params["limit"] = serde_json::json!(limit);
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/sprd/orders-history".to_string(),
+//                                 true,
+//                                 params.to_string(),
+//         ).await;
+//         data
+//     }
+//
+//     //获取历史成交数据(近七天)
+//     pub async fn get_trades(&mut self,
+//                             sprd_id: String,
+//                             trade_id: String,
+//                             ord_id: String,
+//                             begin: String,
+//                             end: String,
+//                             limit: String,
+//     ) -> ResponseData {
+//         let mut params = serde_json::json!({
+//          });
+//         if sprd_id.len() > 0 {
+//             params["sprdId"] = serde_json::json!(sprd_id);
+//         }
+//         if trade_id.len() > 0 {
+//             params["tradeId"] = serde_json::json!(trade_id);
+//         }
+//         if ord_id.len() > 0 {
+//             params["ordId"] = serde_json::json!(ord_id);
+//         }
+//         if begin.len() > 0 {
+//             params["begin"] = serde_json::json!(begin);
+//         }
+//         if end.len() > 0 {
+//             params["end"] = serde_json::json!(end);
+//         }
+//         if limit.len() > 0 {
+//             params["limit"] = serde_json::json!(limit);
+//         }
+//         let data = self.request("GET".to_string(),
+//                                 "/api/v5".to_string(),
+//                                 "/sprd/trades".to_string(),
+//                                 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();
+//     }
+//     //调用请求
+//     pub async fn request(&mut self,
+//                          method: 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();
+//         let mut passphrase = "".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();
+//         }
+//         if self.login_param.contains_key("pass_key") {
+//             passphrase = self.login_param.get("pass_key").unwrap().to_string();
+//         }
+//         let mut is_login_param = true;
+//         if access_key == "" || secret_key == "" || passphrase == "" {
+//             is_login_param = false
+//         }
+//
+//
+//         //请求头配置-如果需要登录则存在额外配置
+//         let mut body = "".to_string();
+//         let timestamp = Self::get_timestamp();
+//         let mut headers = HeaderMap::new();
+//         headers.insert("Content-Type", "application/json".parse().unwrap());
+//         if method == "POST" {
+//             body = params.clone();
+//         }
+//
+//
+//         //是否需要登录-- 组装sing
+//         if is_login {
+//             if !is_login_param {
+//                 let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+//                 return e;
+//             } else {
+//                 //需要登录-且登录参数齐全
+//                 trace!("param:{}", params);
+//                 trace!("body:{}", body);
+//                 //组装sing
+//                 let sing = Self::sign(secret_key.clone(),
+//                                       method.clone(),
+//                                       prefix_url.clone(),
+//                                       request_url.clone(),
+//                                       params.clone(),
+//                                       body.clone(),
+//                                       timestamp.clone(),
+//                 );
+//                 //组装header
+//                 headers.extend(Self::headers(sing, timestamp, passphrase, access_key));
+//             }
+//         }
+//
+//
+//         // trace!("headers:{:?}", headers);
+//         let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+//         let start_time = chrono::Utc::now().timestamp_millis();
+//         let get_response = self.http_tool(
+//             format!("{}{}", prefix_url.clone(), request_url.clone()),
+//             method.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();
+//         let res_data = Self::res_data_analysis(get_response, base_url, params);
+//         res_data
+//     }
+//
+//     pub fn headers(sign: String, timestamp: String, passphrase: String, access_key: String) -> HeaderMap {
+//         let mut headers = HeaderMap::new();
+//         headers.insert("OK-ACCESS-KEY", access_key.parse().unwrap());
+//         headers.insert("OK-ACCESS-SIGN", sign.parse().unwrap());
+//         headers.insert("OK-ACCESS-TIMESTAMP", timestamp.parse().unwrap());
+//         headers.insert("OK-ACCESS-PASSPHRASE", passphrase.parse().unwrap());
+//         headers
+//     }
+//     pub fn sign(secret_key: String,
+//                 method: String, prefix_url: String, request_url: String,
+//                 params: String, body: String, timestamp: String) -> String
+//     {
+//         /*签名生成*/
+//         let url_param_str = RestTool::parse_params_to_str(params);
+//         let base_url = if method == "GET" {
+//             format!("{}{}?{}", prefix_url, request_url, url_param_str)
+//         } else {
+//             format!("{}{}", prefix_url, request_url)
+//         };
+//
+//         // 时间戳 + 请求类型+ 请求参数字符串
+//         let message = format!("{}{}{}{}", timestamp, method, base_url, body);
+//         trace!("message:{}",message);
+//
+//         // 做签名
+//         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);
+//         sign
+//     }
+//
+//     async fn http_tool(&mut 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: String = if RestTool::parse_params_to_str(params.clone()) == "" {
+//             url.clone()
+//         } else {
+//             format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()))
+//         };
+//         trace!("url:{}", url);
+//         trace!("addrs_url:{}", addrs_url);
+//         let params_json: serde_json::Value = serde_json::from_str(&params).unwrap();
+//         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(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(self.label.clone(), 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(self.label.clone(), "200".to_string(), "success".to_string(), body);
+//         } else {
+//             let body = response.text().await?;
+//             // trace!("error-----{}", body);
+//             res_data = ResponseData::error(self.label.clone(), body.to_string())
+//         }
+//
+//         Ok(res_data)
+//     }
+//
+//
+//     //res_data 解析
+//     pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
+//         // trace!("原始数据:{:?}",result);
+//         match result {
+//             Ok(res_data) => {
+//                 if res_data.code != "200" {
+//                     // trace!("不等于200");
+//                     let message: String = res_data.message;
+//                     let json_value: serde_json::Value = serde_json::from_str(&message).unwrap();
+//                     let code = json_value["code"].as_str().unwrap();
+//                     let msg = json_value["msg"].as_str().unwrap();
+//                     let error = ResponseData::new("".to_string(),
+//                                                   format!("{}", code),
+//                                                   format!("{}", msg),
+//                                                   format!("请求地址:{},请求参数:{}", base_url, params));
+//                     error
+//                 } else {
+//                     // trace!("等于200");
+//                     let body: String = res_data.data;
+//                     let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+//
+//                     let code = json_value["code"].as_str().unwrap();
+//                     if code == "0" {
+//                         let data = serde_json::to_string(&json_value["data"]).unwrap();
+//                         let success = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), data.parse().unwrap());
+//                         success
+//                     } else if code == "1" || code == "0" {
+//                         let msg = json_value["msg"].as_str().unwrap();
+//                         let data = serde_json::to_string(&json_value["data"]).unwrap();
+//
+//                         let data_json: serde_json::Value = serde_json::from_str(&data).unwrap();
+//                         let code_v = data_json[0]["sCode"].as_str().unwrap();
+//                         let msg_v = data_json[0]["sMsg"].as_str().unwrap();
+//                         // trace!("发生错误:??{:?}",data_json.to_string());
+//                         let error = ResponseData::new("".to_string(),
+//                                                       format!("{}", code_v),
+//                                                       format!("{}:{}", msg, msg_v),
+//                                                       format!("请求地址:{},请求参数:{}", base_url, params));
+//                         error
+//                     } else {
+//                         let msg = json_value["msg"].as_str().unwrap();
+//                         // trace!("发生错误:??{:?}",data_json.to_string());
+//                         let error = ResponseData::new("".to_string(),
+//                                                       format!("{}", code),
+//                                                       format!("{}", msg),
+//                                                       format!("请求地址:{},请求参数:{}", base_url, params));
+//                         error
+//                     }
+//                 }
+//             }
+//             Err(err) => {
+//                 let error = ResponseData::error("".to_string(), format!("json 解析失败:{}", err));
+//                 error
+//             }
+//         }
+//     }
+//     fn get_timestamp() -> String {
+//         chrono::Utc::now()
+//             .format("%Y-%m-%dT%H:%M:%S%.3fZ")
+//             .to_string()
+//     }
+// }

+ 371 - 0
exchanges/src/okx_swap_ws.rs

@@ -0,0 +1,371 @@
+// use std::sync::Arc;
+// use std::sync::atomic::AtomicBool;
+// use std::time::Duration;
+//
+// use chrono::Utc;
+// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+// use ring::hmac;
+// use serde_json::{json, Value};
+// use tokio::sync::Mutex;
+// use tokio_tungstenite::tungstenite::{Error, Message};
+// use tracing::{info, trace};
+//
+// use crate::response_base::ResponseData;
+// use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+//
+// //类型
+// pub enum OkxSwapWsType {
+//     //订阅频道类型
+//     Public,
+//     Private,
+//     Business,
+// }
+//
+// //订阅频道
+// #[derive(Clone)]
+// pub enum OkxSwapSubscribeType {
+//     PuIndexTickers,
+//     PuBooks5,
+//     Putrades,
+//     PuBooks50L2tbt,
+//
+//     BuIndexCandle30m,
+//
+//     PrBalanceAndPosition,
+//     PrAccount(String),
+//     PrOrders,
+//     PrPositions,
+//
+// }
+//
+// //账号信息
+// #[derive(Clone)]
+// #[allow(dead_code)]
+// pub struct OkxSwapLogin {
+//     pub api_key: String,
+//     pub secret_key: String,
+//     pub passphrase: String,
+// }
+//
+// #[derive(Clone)]
+// pub struct OkxSwapWs {
+//     //类型
+//     label: String,
+//     //地址
+//     address_url: String,
+//     //账号信息
+//     login_param: Option<OkxSwapLogin>,
+//     //币对
+//     symbol_s: Vec<String>,
+//     //订阅
+//     subscribe_types: Vec<OkxSwapSubscribeType>,
+//     //心跳间隔
+//     heartbeat_time: u64,
+// }
+//
+// impl OkxSwapWs {
+//     /*******************************************************************************************************/
+//     /*****************************************获取一个对象****************************************************/
+//     /*******************************************************************************************************/
+//     pub fn new(is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
+//         return OkxSwapWs::new_label("default-OkxSwapWs".to_string(), is_colo, login_param, ws_type);
+//     }
+//     pub fn new_label(label: String, is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
+//         /*******公共频道-私有频道数据组装*/
+//         let address_url = match ws_type {
+//             OkxSwapWsType::Public => {
+//                 "wss://ws.okx.com:8443/ws/v5/public".to_string()
+//             }
+//             OkxSwapWsType::Private => {
+//                 "wss://ws.okx.com:8443/ws/v5/private".to_string()
+//             }
+//             OkxSwapWsType::Business => {
+//                 "wss://ws.okx.com:8443/ws/v5/business".to_string()
+//             }
+//         };
+//
+//         if is_colo {
+//             info!("开启高速(未配置,走普通:{})通道",address_url);
+//         } else {
+//             info!("走普通通道:{}",address_url);
+//         }
+//         /*****返回结构体*******/
+//         OkxSwapWs {
+//             label,
+//             address_url,
+//             login_param,
+//             symbol_s: vec![],
+//             subscribe_types: vec![],
+//             heartbeat_time: 1000 * 10,
+//         }
+//     }
+//
+//     /*******************************************************************************************************/
+//     /*****************************************订阅函数********************************************************/
+//     /*******************************************************************************************************/
+//     //手动添加订阅信息
+//     pub fn set_subscribe(&mut self, subscribe_types: Vec<OkxSwapSubscribeType>) {
+//         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 {
+//                 OkxSwapSubscribeType::PuIndexTickers => false,
+//                 OkxSwapSubscribeType::PuBooks5 => false,
+//                 OkxSwapSubscribeType::Putrades => false,
+//                 OkxSwapSubscribeType::PuBooks50L2tbt => false,
+//
+//                 OkxSwapSubscribeType::BuIndexCandle30m => false,
+//
+//                 OkxSwapSubscribeType::PrBalanceAndPosition => true,
+//                 OkxSwapSubscribeType::PrAccount(_) => true,
+//                 OkxSwapSubscribeType::PrOrders => true,
+//                 OkxSwapSubscribeType::PrPositions => true,
+//             } {
+//                 return true;
+//             }
+//         }
+//         false
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************工具函数********************************************************/
+//     /*******************************************************************************************************/
+//     //订阅枚举解析
+//     pub fn enum_to_string(symbol: String, subscribe_type: OkxSwapSubscribeType) -> Value {
+//         match subscribe_type {
+//             OkxSwapSubscribeType::PuIndexTickers => {
+//                 json!({
+//                     "channel":"index-tickers",
+//                     "instId":symbol
+//                 })
+//             }
+//
+//             OkxSwapSubscribeType::PuBooks5 => {
+//                 json!({
+//                     "channel":"books5",
+//                     "instId":symbol
+//                 })
+//             }
+//             OkxSwapSubscribeType::Putrades => {
+//                 json!({
+//                     "channel":"trades",
+//                     "instId":symbol
+//                 })
+//             }
+//
+//             OkxSwapSubscribeType::BuIndexCandle30m => {
+//                 json!({
+//                     "channel":"index-candle30m",
+//                     "instId":symbol
+//                 })
+//             }
+//
+//             OkxSwapSubscribeType::PrAccount(ccy) => {
+//                 json!({
+//                     "channel":"account",
+//                     "ccy":ccy
+//                 })
+//             }
+//             OkxSwapSubscribeType::PuBooks50L2tbt => {
+//                 json!({
+//                     "channel":"books50-l2-tbt",
+//                     "instId":symbol
+//                 })
+//             }
+//             OkxSwapSubscribeType::PrBalanceAndPosition => {
+//                 json!({
+//                     "channel":"balance_and_position"
+//                 })
+//             }
+//             OkxSwapSubscribeType::PrOrders => {
+//                 json!({
+//                     "channel":"orders",
+//                     "instType":"SWAP",
+//                     "instFamily":symbol
+//                 })
+//             }
+//             OkxSwapSubscribeType::PrPositions => {
+//                 json!({
+//                     "channel":"positions",
+//                     "instType":"SWAP",
+//                 })
+//             }
+//         }
+//     }
+//     //订阅信息生成
+//     pub fn get_subscription(&self) -> String {
+//         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());
+//                 args.push(ty_str);
+//             }
+//         }
+//         let str = json!({
+//             "op": "subscribe",
+//             "args": args
+//         });
+//
+//         // trace!("订阅信息:{}", str.to_string());
+//
+//         str.to_string()
+//     }
+//     //登录组装
+//     fn log_in_to_str(login_param: Option<OkxSwapLogin>) -> String {
+//         let mut login_json_str = "".to_string();
+//
+//         let mut access_key: String = "".to_string();
+//         let mut secret_key: String = "".to_string();
+//         let mut passphrase: String = "".to_string();
+//
+//
+//         if let Some(param) = login_param {
+//             access_key = param.api_key;
+//             secret_key = param.secret_key;
+//             passphrase = param.passphrase;
+//         }
+//
+//         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
+//     }
+//     /*******************************************************************************************************/
+//     /*****************************************socket基本*****************************************************/
+//     /*******************************************************************************************************/
+//     //链接
+//     pub async fn ws_connect_async(&mut self,
+//                                   is_shutdown_arc: Arc<AtomicBool>,
+//                                   write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+//                                   write_rx: UnboundedReceiver<Message>,
+//                                   read_tx: UnboundedSender<ResponseData>) -> Result<(), Error>
+//     {
+//         let login_is = self.contains_pr();
+//         let subscription = self.get_subscription();
+//         let address_url = self.address_url.clone();
+//         let label = self.label.clone();
+//         let heartbeat_time = self.heartbeat_time.clone();
+//
+//
+//         //心跳-- 方法内部线程启动
+//         let write_tx_clone1 = Arc::clone(write_tx_am);
+//         tokio::spawn(async move {
+//             trace!("线程-异步心跳-开始");
+//             AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time).await;
+//             trace!("线程-异步心跳-结束");
+//         });
+//
+//         //设置订阅
+//         let  subscribe_array = vec![];
+//         if login_is {
+//             let write_tx_clone2 = Arc::clone(write_tx_am);
+//             let login_str = Self::log_in_to_str(self.login_param.clone());
+//             tokio::spawn(async move {
+//                 //登录相关
+//                 AbstractWsMode::send_subscribe(write_tx_clone2, Message::Text(login_str)).await;
+//             });
+//         }
+//         let write_tx_clone3 = Arc::clone(write_tx_am);
+//         tokio::spawn(async move {
+//             tokio::time::sleep(Duration::from_millis(3 * 1000)).await;
+//             //登录相关
+//             AbstractWsMode::send_subscribe(write_tx_clone3, Message::Text(subscription)).await;
+//         });
+//
+//         //链接
+//         let t2 = tokio::spawn(async move {
+//             trace!("线程-异步链接-开始");
+//             match AbstractWsMode::ws_connect_async(is_shutdown_arc, address_url.clone(),
+//                                              label.clone(), subscribe_array,
+//                                              write_rx, read_tx,
+//                                              Self::message_text,
+//                                              Self::message_ping,
+//                                              Self::message_pong,
+//             ).await{
+//                 Ok(_) => { trace!("线程-异步链接-结束"); }
+//                 Err(e) => { trace!("发生异常:okx-期货链接关闭-{:?}",e); }
+//             }
+//         });
+//         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".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析-pong
+//     pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+//         return Option::from(ResponseData::new("".to_string(), "-301".to_string(), "success".to_string(), "".to_string()));
+//     }
+//     //数据解析
+//     pub fn ok_text(text: String) -> ResponseData
+//     {
+//         // trace!("元数据:{}",text);
+//         let mut res_data = ResponseData::new("".to_string(), "".to_string(), "success".to_string(), "".to_string());
+//         let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+//         if json_value.get("event").is_some() {//订阅返回
+//             if json_value["event"].as_str() == Option::from("login") &&
+//                 json_value["code"].as_str() == Option::from("0") {
+//                 res_data.code = "-200".to_string();
+//                 res_data.message = format!("登录成功!");
+//             } else if json_value["event"].as_str() == Option::from("error") {
+//                 res_data.code = json_value["code"].to_string();
+//                 res_data.message = format!("订阅失败:{}", json_value["msg"].to_string());
+//             } else if json_value["event"].as_str() == Option::from("subscribe") {
+//                 res_data.code = "-201".to_string();
+//                 res_data.data = text;
+//                 res_data.message = format!("订阅成功!");
+//             }
+//         } else {
+//             if json_value.get("arg").is_some() && json_value.get("data").is_some() {
+//                 res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+//                 res_data.data = json_value["data"].to_string();
+//                 res_data.code = "200".to_string();
+//                 // res_data.reach_time = json_value["data"][0]["ts"].as_str().unwrap().parse().unwrap()
+//             } else {
+//                 res_data.data = text;
+//                 res_data.channel = "未知频道".to_string();
+//             }
+//         }
+//         res_data
+//     }
+// }

+ 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 label: 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(label: String, code: i16, message: String, data: Value) -> ResponseData {
+        ResponseData {
+            label,
+            code,
+            message,
+            data,
+            channel: "".to_string(),
+            time: 0,
+            reach_time: 0,
+            data_type: String::new(),
+            ins: Instant::now(),
+        }
+    }
+    pub fn error(label: String, message: String) -> ResponseData {
+        ResponseData {
+            label,
+            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)
+    }
+}
+

+ 426 - 0
exchanges/src/socket_tool.rs

@@ -0,0 +1,426 @@
+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, Future>(write_to_socket_rx_arc: Arc<Mutex<UnboundedReceiver<Message>>>,
+                                                    is_first_login: bool,
+                                                    label: 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)
+        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,
+              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.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("订阅消息失败");
+            }
+        }
+
+        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);
+                // let response_data = func(message);
+                if response_data.is_some() {
+                    let mut data = response_data.unwrap();
+                    data.label = label.clone();
+
+                    let code = data.code.clone();
+
+                    if code == 200 {
+                        let mut data_c = data.clone();
+                        data_c.ins = Instant::now();
+
+                        handle_function(data_c).await;
+                    }
+
+                    /*
+                        200 -正确返回
+                       -200 -登录成功
+                       -201 -订阅成功
+                       -300 -客户端收到服务器心跳ping,需要响应
+                       -301 -客户端收到服务器心跳pong,需要响应
+                       -302 -客户端收到服务器心跳自定义,需要响应自定义
+                    */
+                    match code {
+                        -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("订阅消息失败");
+                            }
+                        }
+                        -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, Future>(is_shutdown_arc: Arc<AtomicBool>,
+                                                        handle_function: F,
+                                                        address_url: String,
+                                                        is_first_login: bool,
+                                                        label: String,
+                                                        subscribe_array: Vec<String>,
+                                                        write_to_socket_rx_arc: Arc<Mutex<UnboundedReceiver<Message>>>,
+                                                        message_text: T,
+                                                        message_ping: PI,
+                                                        message_pong: PO)
+        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,
+              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,
+                                   label,
+                                   is_shutdown_arc,
+                                   &handle_function,
+                                   subscribe_array.clone(),
+                                   ws_stream,
+                                   message_text,
+                                   message_ping,
+                                   message_pong).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>(message: Result<Message, Error>,
+                                       message_text: T,
+                                       message_ping: PI,
+                                       message_pong: PO) -> Option<ResponseData>
+        where T: Fn(String) -> Option<ResponseData>,
+              PI: Fn(Vec<u8>) -> Option<ResponseData>,
+              PO: 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)) => {
+                //二进制WebSocket消息
+                let message_str = format!("Binary:{:?}", s);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+            }
+            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
+}

+ 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
+}

+ 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
+}

+ 411 - 0
exchanges/tests/okx_swap_test.rs

@@ -0,0 +1,411 @@
+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::okx_swap_rest::OkxSwapRest;
+use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+
+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, OkxSwapWsType::Public).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        // OkxSwapSubscribeType::PuBooks5,
+        // OkxSwapSubscribeType::Putrades,
+        OkxSwapSubscribeType::PuBooks50L2tbt,
+        // OkxSwapSubscribeType::PuIndexTickers,
+    ]);
+
+
+    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_bu() {
+    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, OkxSwapWsType::Business).await;
+    ws.set_symbols(vec!["BTC-USD".to_string()]);
+    ws.set_subscribe(vec![
+        OkxSwapSubscribeType::BuIndexCandle30m,
+    ]);
+
+    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 btree_map = OkxSwapLogin {
+        api_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        passphrase: PASS_KEY.to_string(),
+    };
+    let mut ws = get_ws(Option::from(btree_map), OkxSwapWsType::Private).await;
+    ws.set_symbols(vec!["BTC-USDT".to_string()]);
+    ws.set_subscribe(vec![
+        OkxSwapSubscribeType::PrAccount("USDT".to_string()),
+        OkxSwapSubscribeType::PrOrders,
+        OkxSwapSubscribeType::PrPositions,
+        OkxSwapSubscribeType::PrBalanceAndPosition,
+    ]);
+
+
+    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;
+    //
+    // 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(), 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 (tx, mut rx) = channel(1024);
+    // let mut ws = get_ws(btree_map, OkxWsType::Private, tx).await;
+    // ws.set_subscribe(vec![
+    //     OkxSubscribeType::PrBalanceAndPosition,
+    //     // OkxSubscribeType::PrAccount("USDT".to_string()),
+    //     OkxSubscribeType::PrOrders,
+    //     OkxSubscribeType::PrPositions,
+    // ]);
+    //
+    // let t1 = tokio::spawn(async move {
+    //     ws.custom_subscribe(is_shutdown_arc, vec!["BTC-USDT".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();
+}
+
+
+//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("BTC-USDT".to_string(), "3333".to_string(), "".to_string()).await;
+    println!("okx--订单查询--{:?}", req_data);
+}
+
+
+//rest-未完成的订单
+#[tokio::test]
+async fn rest_get_incomplete_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_incomplete_order("BTC-USDT".to_string()).await;
+    println!("okx--未完成的订单--{:?}", req_data);
+}
+
+//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!("okx--获取系统时间--{:?}", 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("SWA1P".to_string()).await;
+    println!("okx--查看持仓信息--{:?}", req_data);
+}
+
+//rest-获取单个产品行情信息
+#[tokio::test]
+async fn rest_get_ticker_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_ticker("BTC-USDT".to_string()).await;
+    println!("okx--获取单个产品行情信息--{:?}", req_data);
+}
+
+//rest-查看账户余额
+#[tokio::test]
+async fn rest_get_balance_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_balance("BTC,ETH".to_string()).await;
+    println!("okx--查看账户余额--{:?}", req_data);
+}
+
+//rest-获取交易产品基础信息
+#[tokio::test]
+async fn rest_get_instruments_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_instruments().await;
+    println!("okx--获取交易产品基础信息--{:?}", req_data);
+}
+
+//rest-获取成交明细(近三天)
+#[tokio::test]
+async fn rest_get_trade_fills_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_trade_fills("".to_string()).await;
+    println!("okx--获取成交明细(近三天)--{:?}", 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("BTC-USD".to_string(), "1111".to_string(), "".to_string()).await;
+    println!("okx--撤单--{:?}", 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("BTC-USDT".to_string(), "5".to_string()).await;
+    println!("okx--设置杠杆倍数--{:?}", 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().await;
+    println!("okx--设置持仓模式--{:?}", req_data);
+}
+
+//rest-获取历史订单记录(近七天)
+#[tokio::test]
+async fn rest_get_orders_history_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_orders_history("".to_string(),
+                                          "".to_string(),
+                                          "filled".to_string(),
+                                          "".to_string(),
+                                          "".to_string(),
+                                          "".to_string(),
+    ).await;
+    println!("okx--获取历史订单记录--{:?}", req_data);
+}
+
+//rest-获取历史成交数据(近七天)
+#[tokio::test]
+async fn rest_get_trades_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_trades("".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "100".to_string(),
+    ).await;
+    println!("okx--获取历史成交数据--{:?}", req_data);
+}
+
+
+async fn get_ws(btree_map: Option<OkxSwapLogin>, type_v: OkxSwapWsType) -> OkxSwapWs {
+    let ku_ws = OkxSwapWs::new(false, btree_map, type_v);
+    ku_ws
+}
+
+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();
+// }

+ 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"

+ 118 - 0
standard/src/binance_spot.rs

@@ -0,0 +1,118 @@
+// use std::collections::BTreeMap;
+// use std::io::{Error, ErrorKind};
+// use std::result::Result;
+// use async_trait::async_trait;
+// use rust_decimal::Decimal;
+// use rust_decimal_macros::dec;
+// use tracing::{warn};
+// use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand};
+// use exchanges::binance_spot_rest::BinanceSpotRest;
+// use global::trace_stack::TraceStack;
+//
+// #[allow(dead_code)]
+// #[derive(Clone)]
+// pub struct BinanceSpot {
+//     exchange: ExchangeEnum,
+//     symbol: String,
+//     is_colo: bool,
+//     params: BTreeMap<String, String>,
+//     request: BinanceSpotRest,
+// }
+//
+// impl BinanceSpot {
+//     pub fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>) -> BinanceSpot {
+//         BinanceSpot {
+//             exchange: ExchangeEnum::BinanceSpot,
+//             symbol: symbol.to_uppercase(),
+//             is_colo,
+//             params: params.clone(),
+//             request: BinanceSpotRest::new(is_colo, params.clone()),
+//         }
+//     }
+// }
+//
+//
+// #[async_trait]
+// impl Platform for BinanceSpot {
+//     // 克隆方法
+//     fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+//     // 获取交易所模式
+//     fn get_self_exchange(&self) -> ExchangeEnum {
+//         ExchangeEnum::BinanceSpot
+//     }
+//     // 获取交易对
+//     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()
+//     }
+//
+//     fn get_self_market(&self) -> Market {
+//         warn!("binance_spot:该交易所方法未实现");
+//         return Market::new();
+//     }
+//
+//     fn get_request_delays(&self) -> Vec<i64> {
+//         warn!("binance_spot:该交易所方法未实现");
+//         return vec![];
+//     }
+//
+//     fn get_request_avg_delay(&self) -> Decimal {
+//         warn!("binance_spot:该交易所方法未实现");
+//         return dec!(0);
+//     }
+//
+//     fn get_request_max_delay(&self) -> i64 {
+//         warn!("binance_spot:该交易所方法未实现");
+//         return 0;
+//     }
+//
+//     async fn get_server_time(&mut self) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     // 获取账号信息
+//     async fn get_account(&mut self) -> Result<Account, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     // 获取仓位信息
+//     async fn get_position(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     // 获取市场行情
+//     async fn get_ticker(&mut self) -> Result<Ticker, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_market(&mut self) -> Result<Market, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_market_symbol(&mut self, _symbol: String) -> Result<Market, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".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> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn command_order(&mut self, _order_command: OrderCommand, _trace_stack: TraceStack) { warn!("binance_spot:该交易所方法未实现"); }
+// }

+ 51 - 0
standard/src/binance_spot_handle.rs

@@ -0,0 +1,51 @@
+// use std::str::FromStr;
+// use rust_decimal::Decimal;
+// use rust_decimal_macros::dec;
+// use exchanges::response_base::ResponseData;
+// use crate::{MarketOrder, SpecialDepth, SpecialTicker};
+// use crate::exchange::ExchangeEnum;
+// use crate::handle_info::HandleSwapInfo;
+//
+//
+// // 处理特殊Ticker信息
+// pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+//     let res_data_str = res_data.data;
+//     let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
+//     format_special_ticker(res_data_json, res_data.label)
+// }
+//
+// pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+//     let bp = Decimal::from_str(data["b"].as_str().unwrap()).unwrap();
+//     let bq = Decimal::from_str(data["B"].as_str().unwrap()).unwrap();
+//     let ap = Decimal::from_str(data["a"].as_str().unwrap()).unwrap();
+//     let aq = Decimal::from_str(data["A"].as_str().unwrap()).unwrap();
+//     let mp = (bp + ap) * dec!(0.5);
+//     let t = Decimal::from_str(&data["u"].to_string()).unwrap();
+//
+//     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at: Default::default() };
+//     let depth_info = vec![bp, bq, ap, aq];
+//     SpecialDepth {
+//         name: label,
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at: Default::default(),
+//     }
+// }
+//
+// // 处理特殊深度数据
+// pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+//     HandleSwapInfo::handle_special_depth(ExchangeEnum::BinanceSpot, res_data)
+// }
+//
+// // 格式化深度信息
+// pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+//     let mut depth_items: Vec<MarketOrder> = vec![];
+//     for value in value.as_array().unwrap() {
+//         depth_items.push(MarketOrder {
+//             price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+//             amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+//         })
+//     }
+//     return depth_items;
+// }

+ 393 - 0
standard/src/binance_swap.rs

@@ -0,0 +1,393 @@
+use std::collections::BTreeMap;
+use std::io::{Error, ErrorKind};
+use std::result::Result;
+use std::str::FromStr;
+use async_trait::async_trait;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use tokio::sync::mpsc::Sender;
+use tokio::time::Instant;
+use tracing::{error, warn};
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, utils, PositionModeEnum};
+use exchanges::binance_swap_rest::BinanceSwapRest;
+use global::trace_stack::TraceStack;
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct BinanceSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: BinanceSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl BinanceSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BinanceSwap {
+        let market = Market::new();
+        let mut binance_swap = BinanceSwap {
+            exchange: ExchangeEnum::BinanceSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: BinanceSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        binance_swap.market = BinanceSwap::get_market(&mut binance_swap).await.unwrap_or(binance_swap.market);
+        return binance_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for BinanceSwap {
+    // 克隆方法
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+    // 获取交易所模式
+    fn get_self_exchange(&self) -> ExchangeEnum {
+        ExchangeEnum::BinanceSwap
+    }
+    // 获取交易对
+    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 = res_data.data;
+            let result = res_data_json["serverTime"].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().await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let balance_info = res_data_json.iter().find(|item| item["asset"].as_str().unwrap().to_string() == symbol_array[1].to_string());
+            match balance_info {
+                None => {
+                    error!("binance_swap:格式化账号信息错误!\nget_account: res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let balance = Decimal::from_str(value["balance"].as_str().unwrap()).unwrap();
+                    let available_balance = Decimal::from_str(value["availableBalance"].as_str().unwrap()).unwrap();
+                    let frozen_balance = balance - available_balance;
+                    let result = Account {
+                        coin: value["asset"].as_str().unwrap().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, "binance_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 获取仓位信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_position_risk(symbol_format).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 res_data = self.request.get_position_risk("".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_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_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.get_book_ticker(symbol_format).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = Ticker {
+                time: res_data_json["time"].as_i64().unwrap(),
+                high: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
+                last: dec!(-1),
+                volume: dec!(-1),
+            };
+            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_format = utils::format_symbol(symbol.clone(), "");
+        let res_data = self.request.get_book_ticker(symbol_format).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let result = Ticker {
+                time: res_data_json["time"].as_i64().unwrap(),
+                high: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
+                last: dec!(-1),
+                volume: dec!(-1),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.get_exchange_info().await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let symbols = res_data_json["symbols"].as_array().unwrap();
+            let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("binance_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_json);
+                    Err(Error::new(ErrorKind::Other, res_data_json.to_string()))
+                }
+                Some(value) => {
+                    let base_asset = value["baseAsset"].as_str().unwrap_or("").to_string();
+                    let quote_asset = value["quoteAsset"].as_str().unwrap_or("").to_string();
+
+                    let filter_array = value["filters"].as_array().unwrap().clone();
+                    let price_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "PRICE_FILTER").unwrap();
+                    let lot_size_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "LOT_SIZE").unwrap();
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset, quote_asset),
+                        base_asset,
+                        quote_asset,
+                        tick_size: Decimal::from_str(&price_filter["tickSize"].as_str().unwrap()).unwrap(),
+                        amount_size: Decimal::from_str(lot_size_filter["stepSize"].as_str().unwrap()).unwrap(),
+                        price_precision: Decimal::from_f64(value["pricePrecision"].as_f64().unwrap()).unwrap(),
+                        amount_precision: Decimal::from_f64(value["quantityPrecision"].as_f64().unwrap()).unwrap(),
+                        min_qty: Decimal::from_str(lot_size_filter["minQty"].as_str().unwrap()).unwrap(),
+                        max_qty: Decimal::from_str(lot_size_filter["maxQty"].as_str().unwrap()).unwrap(),
+                        min_notional: Decimal::from_str(price_filter["minPrice"].as_str().unwrap()).unwrap(),
+                        max_notional: Decimal::from_str(price_filter["maxPrice"].as_str().unwrap()).unwrap(),
+                        ct_val: Decimal::ONE,
+                    };
+                    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_format = utils::format_symbol(symbol.clone(), "");
+        let res_data = self.request.get_exchange_info().await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+            let symbols = res_data_json["symbols"].as_array().unwrap();
+            let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("binance_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_json);
+                    Err(Error::new(ErrorKind::Other, res_data_json.to_string()))
+                }
+                Some(value) => {
+                    let base_asset = value["baseAsset"].as_str().unwrap_or("").to_string();
+                    let quote_asset = value["quoteAsset"].as_str().unwrap_or("").to_string();
+
+                    let filter_array = value["filters"].as_array().unwrap().clone();
+                    let price_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "PRICE_FILTER").unwrap();
+                    let lot_size_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "LOT_SIZE").unwrap();
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset, quote_asset),
+                        base_asset,
+                        quote_asset,
+                        tick_size: Decimal::from_str(&price_filter["tickSize"].as_str().unwrap()).unwrap(),
+                        amount_size: Decimal::from_str(lot_size_filter["stepSize"].as_str().unwrap()).unwrap(),
+                        price_precision: Decimal::from_f64(value["pricePrecision"].as_f64().unwrap()).unwrap(),
+                        amount_precision: Decimal::from_f64(value["quantityPrecision"].as_f64().unwrap()).unwrap(),
+                        min_qty: Decimal::from_str(lot_size_filter["minQty"].as_str().unwrap()).unwrap(),
+                        max_qty: Decimal::from_str(lot_size_filter["maxQty"].as_str().unwrap()).unwrap(),
+                        min_notional: Decimal::from_str(price_filter["minPrice"].as_str().unwrap()).unwrap(),
+                        max_notional: Decimal::from_str(price_filter["maxPrice"].as_str().unwrap()).unwrap(),
+                        ct_val: Decimal::ONE,
+                    };
+                    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_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.get_order(symbol_format, order_id.parse().unwrap_or(-1), custom_id.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json: serde_json::Value = res_data.data;
+
+            let status = res_data_json["status"].as_str().unwrap();
+            let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else {
+                error!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data_json);
+                panic!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data_json)
+            };
+            let result = Order {
+                id: res_data_json["orderId"].to_string(),
+                custom_id: res_data_json["clientOrderId"].as_str().unwrap().parse().unwrap(),
+                price: Decimal::from_str(res_data_json["price"].as_str().unwrap()).unwrap(),
+                amount: Decimal::from_str(res_data_json["origQty"].as_str().unwrap()).unwrap(),
+                deal_amount: Decimal::from_str(res_data_json["executedQty"].as_str().unwrap()).unwrap(),
+                avg_price: Decimal::from_str(res_data_json["avgPrice"].as_str().unwrap()).unwrap(),
+                status: custom_status,
+                order_type: res_data_json["type"].as_str().unwrap().parse().unwrap(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("301 binance_swap".to_string()),
+            };
+            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_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.get_open_orders(symbol_format).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| {
+                let status = item["status"].as_str().unwrap();
+                let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else {
+                    error!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data);
+                    panic!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data)
+                };
+                Order {
+                    id: item["orderId"].to_string(),
+                    custom_id: item["clientOrderId"].as_str().unwrap().parse().unwrap(),
+                    price: Decimal::from_str(item["price"].as_str().unwrap()).unwrap(),
+                    amount: Decimal::from_str(item["origQty"].as_str().unwrap()).unwrap(),
+                    deal_amount: Decimal::from_str(item["executedQty"].as_str().unwrap()).unwrap(),
+                    avg_price: Decimal::from_str(item["avgPrice"].as_str().unwrap()).unwrap(),
+                    status: custom_status,
+                    order_type: item["type"].as_str().unwrap().parse().unwrap(),
+                    trace_stack: TraceStack::new(0, Instant::now()).on_special("331 binance_swap".to_string()),
+                }
+            }).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> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".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> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn command_order(&mut self, _order_command: &mut OrderCommand, _trace_stack: &TraceStack) { warn!("binance_swap:该交易所方法未实现"); }
+}
+
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
+    let mut position_mode = match position["positionSide"].as_str().unwrap_or("") {
+        "BOTH" => PositionModeEnum::Both,
+        "LONG" => PositionModeEnum::Long,
+        "SHORT" => PositionModeEnum::Short,
+        _ => {
+            error!("binance_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("binance_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size = Decimal::from_str(position["positionAmt"].as_str().unwrap()).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["symbol"].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["entryPrice"].as_str().unwrap()).unwrap(),
+        profit: Decimal::from_str(position["unRealizedProfit"].as_str().unwrap()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(position["isolatedMargin"].as_str().unwrap()).unwrap(),
+    }
+}

+ 40 - 0
standard/src/binance_swap_handle.rs

@@ -0,0 +1,40 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use exchanges::response_base::ResponseData;
+use crate::{MarketOrder, SpecialDepth, SpecialTicker};
+
+
+// 处理特殊Ticker信息
+pub fn handle_book_ticker(res_data: &ResponseData) -> SpecialDepth {
+    let bp = Decimal::from_str((*res_data).data["b"].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_str((*res_data).data["B"].as_str().unwrap()).unwrap();
+    let ap = Decimal::from_str((*res_data).data["a"].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_str((*res_data).data["A"].as_str().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+    let t = Decimal::from_str(&(*res_data).data["u"].to_string()).unwrap();
+    let create_at = (*res_data).data["E"].as_i64().unwrap() * 1000;
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: (*res_data).label.clone(),
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+        create_at,
+    }
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}

+ 625 - 0
standard/src/bitget_spot.rs

@@ -0,0 +1,625 @@
+// use std::collections::{BTreeMap, HashMap};
+// use std::io::{Error, ErrorKind};
+// use std::str::FromStr;
+// use tokio::sync::mpsc::Sender;
+// use async_trait::async_trait;
+// use futures::stream::FuturesUnordered;
+// use futures::TryStreamExt;
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::ToPrimitive;
+// use rust_decimal_macros::dec;
+// use serde_json::json;
+// use tokio::time::Instant;
+// use tracing::{error};
+// use exchanges::bitget_spot_rest::BitgetSpotRest;
+// use global::trace_stack::TraceStack;
+// use crate::exchange::ExchangeEnum;
+// use crate::{Account, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
+//
+// #[allow(dead_code)]
+// #[derive(Clone)]
+// pub struct BitgetSpot {
+//     exchange: ExchangeEnum,
+//     symbol: String,
+//     is_colo: bool,
+//     params: BTreeMap<String, String>,
+//     request: BitgetSpotRest,
+//     market: Market,
+//     order_sender: Sender<Order>,
+//     error_sender: Sender<Error>,
+// }
+//
+// impl BitgetSpot {
+//     pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BitgetSpot {
+//         let market = Market::new();
+//         let mut bitget_spot = BitgetSpot {
+//             exchange: ExchangeEnum::BitgetSpot,
+//             symbol: symbol.to_uppercase(),
+//             is_colo,
+//             params: params.clone(),
+//             request: BitgetSpotRest::new(is_colo, params.clone()),
+//             market,
+//             order_sender,
+//             error_sender,
+//         };
+//         bitget_spot.market = BitgetSpot::get_market(&mut bitget_spot).await.unwrap_or(bitget_spot.market);
+//         return bitget_spot;
+//     }
+// }
+//
+// #[async_trait]
+// impl Platform for BitgetSpot {
+//     fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+//
+//     fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::BitgetSpot }
+//
+//     fn get_self_symbol(&self) -> String { self.symbol.clone() }
+//
+//     fn get_self_is_colo(&self) -> bool { self.is_colo }
+//
+//     fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+//
+//     fn get_self_market(&self) -> Market { self.market.clone() }
+//
+//     fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+//
+//     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_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = res_data_json["serverTime"].as_str().unwrap().to_string();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_account(&mut self) -> Result<Account, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+//         let res_data = self.request.get_account_assets().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             let result = res_data_json.iter().map(|item| format_account_info(item.clone())).collect();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_position(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+//         let res_data = self.request.get_tickers(symbol_format).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let ticker_info = res_data_json[0].clone();
+//             let time = (Decimal::from_str(&*ticker_info["ts"].as_str().unwrap()).unwrap() / dec!(1000)).floor().to_i64().unwrap();
+//             let result = Ticker {
+//                 time,
+//                 high: Decimal::from_str(ticker_info["high24h"].as_str().unwrap()).unwrap(),
+//                 low: Decimal::from_str(ticker_info["low24h"].as_str().unwrap()).unwrap(),
+//                 sell: Decimal::from_str(ticker_info["askPr"].as_str().unwrap()).unwrap(),
+//                 buy: Decimal::from_str(ticker_info["bidPr"].as_str().unwrap()).unwrap(),
+//                 last: Decimal::from_str(ticker_info["lastPr"].as_str().unwrap()).unwrap(),
+//                 volume: Decimal::from_str(ticker_info["quoteVolume"].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_format = utils::format_symbol(symbol.clone(), "");
+//         let res_data = self.request.get_tickers(symbol_format).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let ticker_info = res_data_json[0].clone();
+//             let time = (Decimal::from_str(&*ticker_info["ts"].as_str().unwrap()).unwrap() / dec!(1000)).floor().to_i64().unwrap();
+//             let result = Ticker {
+//                 time,
+//                 high: Decimal::from_str(ticker_info["high24h"].as_str().unwrap()).unwrap(),
+//                 low: Decimal::from_str(ticker_info["low24h"].as_str().unwrap()).unwrap(),
+//                 sell: Decimal::from_str(ticker_info["askPr"].as_str().unwrap()).unwrap(),
+//                 buy: Decimal::from_str(ticker_info["bidPr"].as_str().unwrap()).unwrap(),
+//                 last: Decimal::from_str(ticker_info["lastPr"].as_str().unwrap()).unwrap(),
+//                 volume: Decimal::from_str(ticker_info["quoteVolume"].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_format = utils::format_symbol(self.symbol.clone(), "");
+//         let res_data = self.request.get_symbols(symbol_format.clone()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             let market_info = res_data_json.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format.clone());
+//             match market_info {
+//                 None => {
+//                     error!("bitget_spot:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+//                     Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//                 }
+//                 Some(value) => {
+//                     let base_asset = value["baseCoin"].as_str().unwrap().to_string();
+//                     let quote_asset = value["quoteCoin"].as_str().unwrap().to_string();
+//                     let price_precision = Decimal::from_str(value["pricePrecision"].as_str().unwrap()).unwrap();
+//                     let amount_precision = Decimal::from_str(value["quantityPrecision"].as_str().unwrap()).unwrap();
+//                     let min_qty = Decimal::from_str(&value["minTradeAmount"].as_str().unwrap()).unwrap();
+//                     let max_qty = Decimal::from_str(&value["maxTradeAmount"].as_str().unwrap()).unwrap();
+//
+//                     let tick_size = if price_precision > dec!(0) {
+//                         Decimal::from_str(&format!("0.{}1", "0".repeat(usize::try_from(price_precision - dec!(1)).unwrap()))).unwrap()
+//                     } else {
+//                         Decimal::ONE
+//                     };
+//                     let amount_size = if amount_precision > dec!(0) {
+//                         Decimal::from_str(&format!("0.{}1", "0".repeat(usize::try_from(amount_precision - dec!(1)).unwrap()))).unwrap()
+//                     } else {
+//                         Decimal::ONE
+//                     };
+//
+//                     let result = Market {
+//                         symbol: format!("{}_{}", base_asset, quote_asset),
+//                         base_asset,
+//                         quote_asset,
+//                         tick_size,
+//                         amount_size,
+//                         price_precision,
+//                         amount_precision,
+//                         min_qty,
+//                         max_qty,
+//                         min_notional: min_qty,
+//                         max_notional: max_qty,
+//                         ct_val: Decimal::ONE,
+//                     };
+//                     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_format = utils::format_symbol(symbol.clone(), "");
+//         let res_data = self.request.get_symbols(symbol_format.clone()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             let market_info = res_data_json.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format.clone());
+//             match market_info {
+//                 None => {
+//                     error!("bitget_spot:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+//                     Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//                 }
+//                 Some(value) => {
+//                     let base_asset = value["baseCoin"].as_str().unwrap().to_string();
+//                     let quote_asset = value["quoteCoin"].as_str().unwrap().to_string();
+//                     let price_precision = Decimal::from_str(value["pricePrecision"].as_str().unwrap()).unwrap();
+//                     let amount_precision = Decimal::from_str(value["quantityPrecision"].as_str().unwrap()).unwrap();
+//                     let min_qty = Decimal::from_str(&value["minTradeAmount"].as_str().unwrap()).unwrap();
+//                     let max_qty = Decimal::from_str(&value["maxTradeAmount"].as_str().unwrap()).unwrap();
+//
+//                     let tick_size = if price_precision > dec!(0) {
+//                         Decimal::from_str(&format!("0.{}1", "0".repeat(usize::try_from(price_precision - dec!(1)).unwrap()))).unwrap()
+//                     } else {
+//                         Decimal::ONE
+//                     };
+//                     let amount_size = if amount_precision > dec!(0) {
+//                         Decimal::from_str(&format!("0.{}1", "0".repeat(usize::try_from(amount_precision - dec!(1)).unwrap()))).unwrap()
+//                     } else {
+//                         Decimal::ONE
+//                     };
+//
+//                     let result = Market {
+//                         symbol: format!("{}_{}", base_asset, quote_asset),
+//                         base_asset,
+//                         quote_asset,
+//                         tick_size,
+//                         amount_size,
+//                         price_precision,
+//                         amount_precision,
+//                         min_qty,
+//                         max_qty,
+//                         min_notional: min_qty,
+//                         max_notional: max_qty,
+//                         ct_val: Decimal::ONE,
+//                     };
+//                     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 ct_val = self.market.ct_val;
+//         let res_data = self.request.get_order(order_id.to_string(), custom_id.to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             if res_data_json.len() == 0 {
+//                 Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//             } else {
+//                 let order_info = res_data_json[0].clone();
+//                 let result = format_order_item(order_info, 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_format = utils::format_symbol(self.symbol.clone(), "");
+//         let ct_val = self.market.ct_val;
+//         let res_data = self.request.get_unfilled_orders(symbol_format.to_string(), "".to_string(), "".to_string(), "".to_string(), "100".to_string(), "".to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).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 take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+//         let mut params = json!({
+//             "symbol": symbol_format.to_string(),
+//             "clientOid": custom_id,
+//             "price": price.to_string(),
+//         });
+//         if price.eq(&Decimal::ZERO) {
+//             params["orderType"] = json!("market");
+//             params["force"] = json!("post_only");
+//         } else {
+//             params["orderType"] = json!("limit");
+//             params["force"] = json!("gtc");
+//         };
+//         params["size"] = json!(amount);
+//         match origin_side {
+//             "kd" => {
+//                 params["side"] = json!("buy");
+//             }
+//             "pd" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "kk" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "pk" => {
+//                 params["side"] = json!("buy");
+//             }
+//             _ => { error!("下单参数错误"); }
+//         };
+//         let res_data = self.request.spot_order(params).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = Order {
+//                 id: res_data_json["orderId"].as_str().unwrap().to_string(),
+//                 custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "NEW".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("328 bitget_spot".to_string()),
+//             };
+//             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_format = utils::format_symbol(symbol.clone(), "");
+//         let mut params = json!({
+//             "symbol": symbol_format.to_string(),
+//             "clientOid": custom_id,
+//             "price": price.to_string(),
+//         });
+//         if price.eq(&Decimal::ZERO) {
+//             params["orderType"] = json!("market");
+//             params["force"] = json!("post_only");
+//         } else {
+//             params["orderType"] = json!("limit");
+//             params["force"] = json!("gtc");
+//         };
+//         let size = (amount / ct_val).floor();
+//         params["size"] = json!(size);
+//         match origin_side {
+//             "kd" => {
+//                 params["side"] = json!("buy");
+//             }
+//             "pd" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "kk" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "pk" => {
+//                 params["side"] = json!("buy");
+//             }
+//             _ => { error!("下单参数错误"); }
+//         };
+//         let res_data = self.request.spot_order(params).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = Order {
+//                 id: res_data_json["orderId"].as_str().unwrap().to_string(),
+//                 custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "NEW".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("380 bitget_spot".to_string()),
+//             };
+//             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_format = utils::format_symbol(self.symbol.clone(), "");
+//         let res_data = self.request.spot_cancel_order(symbol_format.clone(), order_id.to_string(), custom_id.to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = Order {
+//                 id: res_data_json["orderId"].as_str().unwrap().to_string(),
+//                 custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "REMOVE".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("403 bitget_spot".to_string()),
+//             };
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+//         let orders_res_data = self.request.get_unfilled_orders(symbol_format.to_string(), "".to_string(), "".to_string(), "".to_string(), "100".to_string(), "".to_string()).await;
+//         if orders_res_data.code == "200" {
+//             let mut result = vec![];
+//             let orders_res_data_str = &orders_res_data.data;
+//             let orders_res_data_json: Vec<serde_json::Value> = serde_json::from_str(orders_res_data_str).unwrap();
+//             for order in orders_res_data_json {
+//                 let order_id = order["orderId"].as_str().unwrap().to_string();
+//                 let cancel_res_data = self.request.spot_cancel_order(symbol_format.clone(), order_id, "".to_string()).await;
+//                 if cancel_res_data.code == "200" {
+//                     let cancel_res_data_str = &cancel_res_data.data;
+//                     let cancel_res_data_json: serde_json::Value = serde_json::from_str(cancel_res_data_str).unwrap();
+//                     result.push(Order {
+//                         id: cancel_res_data_json["orderId"].as_str().unwrap().to_string(),
+//                         custom_id: cancel_res_data_json["clientOid"].as_str().unwrap().to_string(),
+//                         price: Decimal::ZERO,
+//                         amount: Decimal::ZERO,
+//                         deal_amount: Decimal::ZERO,
+//                         avg_price: Decimal::ZERO,
+//                         status: "REMOVE".to_string(),
+//                         order_type: "".to_string(),
+//                         trace_stack: TraceStack::new(0, Instant::now()).on_special("434 bitget_spot".to_string()),
+//                     });
+//                 } 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 cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bitget_spot:该交易所方法未实现".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_uppercase();
+//         let res_data = self.request.wallet_transfer(from.to_string(), to.to_string(), amount.to_string(), coin_format.clone(), "".to_string(), "".to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let result = res_data_str.clone();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
+//         let mut handles = vec![];
+//         // 撤销订单
+//         let cancel = order_command.cancel;
+//         for item in cancel.keys() {
+//             let mut self_clone = self.clone();
+//             let cancel_clone = cancel.clone();
+//             let item_clone = item.clone();
+//             let order_id = cancel_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+//             let custom_id = cancel_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let handle = tokio::spawn(async move {
+//                 let result = self_clone.cancel_order(&order_id, &custom_id).await;
+//                 match result {
+//                     Ok(_) => {
+//                         // result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         // 取消失败去查订单。
+//                         let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+//                         match query_rst {
+//                             Ok(order) => {
+//                                 result_sd.send(order).await.unwrap();
+//                             }
+//                             Err(_query_err) => {
+//                                 // error!(?_query_err);
+//                                 // error!("撤单失败,而且查单也失败了,bitget_spot,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+//                             }
+//                         }
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//         // 下单指令
+//         let mut limits = HashMap::new();
+//         limits.extend(order_command.limits_open);
+//         limits.extend(order_command.limits_close);
+//         for item in limits.keys() {
+//             let mut self_clone = self.clone();
+//             let limits_clone = limits.clone();
+//             let item_clone = item.clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let ts = trace_stack.clone();
+//
+//             let handle = tokio::spawn(async move {
+//                 let value = limits_clone[&item_clone].clone();
+//                 let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
+//                 let side = value.get(1).unwrap();
+//                 let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
+//                 let cid = value.get(3).unwrap();
+//
+//                 //  order_name: [数量,方向,价格,c_id]
+//                 let result = self_clone.take_order(cid, side, price, amount).await;
+//                 match result {
+//                     Ok(mut result) => {
+//                         // 记录此订单完成时间
+//                         // ts.on_after_send();
+//                         result.trace_stack = ts;
+//
+//                         result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         let mut err_order = Order::new();
+//                         err_order.custom_id = cid.clone();
+//                         err_order.status = "REMOVE".to_string();
+//
+//                         result_sd.send(err_order).await.unwrap();
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//         // 检查订单指令
+//         let check = order_command.check;
+//         for item in check.keys() {
+//             let mut self_clone = self.clone();
+//             let check_clone = check.clone();
+//             let item_clone = item.clone();
+//             let order_id = check_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+//             let custom_id = check_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let handle = tokio::spawn(async move {
+//                 let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+//                 match result {
+//                     Ok(result) => {
+//                         result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//
+//         let futures = FuturesUnordered::from_iter(handles);
+//         let _: Result<Vec<_>, _> = futures.try_collect().await;
+//     }
+// }
+//
+// pub fn format_account_info(balance_data: serde_json::Value) -> Account {
+//     let balance_coin = balance_data["coin"].as_str().unwrap().to_string().to_uppercase();
+//     let available_balance = Decimal::from_str(balance_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_balance = Decimal::from_str(balance_data["frozen"].as_str().unwrap()).unwrap();
+//     let balance = available_balance + frozen_balance;
+//
+//     Account {
+//         coin: balance_coin,
+//         balance,
+//         available_balance,
+//         frozen_balance,
+//         stocks: Decimal::ZERO,
+//         available_stocks: Decimal::ZERO,
+//         frozen_stocks: Decimal::ZERO,
+//     }
+// }
+//
+// pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+//     let price = Decimal::from_str(order["price"].as_str().unwrap_or(order["priceAvg"].as_str().unwrap())).unwrap();
+//     let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
+//     let status = order["status"].as_str().unwrap_or("");
+//     let base_volume = Decimal::from_str(order["baseVolume"].as_str().unwrap()).unwrap();
+//
+//     let avg_price = Decimal::from_str(order["priceAvg"].as_str().unwrap()).unwrap();
+//
+//     let amount = size * ct_val;
+//     let deal_amount = base_volume * ct_val;
+//     let custom_status = if ["filled", "cancelled"].contains(&status) {
+//         "REMOVE".to_string()
+//     } else if ["init", "live", "new", "partially_filled"].contains(&status) {
+//         "NEW".to_string()
+//     } else {
+//         "NULL".to_string()
+//     };
+//     Order {
+//         id: order["orderId"].as_str().unwrap().to_string(),
+//         custom_id: order["clientOid"].as_str().unwrap().to_string(),
+//         price,
+//         amount,
+//         deal_amount,
+//         avg_price,
+//         status: custom_status,
+//         order_type: order["orderType"].as_str().unwrap().to_string(),
+//         trace_stack: TraceStack::new(0, Instant::now()).on_special("622 bitget_spot".to_string()),
+//     }
+// }

+ 136 - 0
standard/src/bitget_spot_handle.rs

@@ -0,0 +1,136 @@
+// use std::str::FromStr;
+// use rust_decimal::Decimal;
+// use rust_decimal_macros::dec;
+// use serde_json::json;
+// use tokio::time::Instant;
+// use tracing::trace;
+// use exchanges::response_base::ResponseData;
+// use global::trace_stack::TraceStack;
+// use crate::{Account, MarketOrder, Order, SpecialDepth, SpecialOrder, SpecialTicker};
+// use crate::exchange::ExchangeEnum;
+// use crate::handle_info::HandleSwapInfo;
+//
+// // 处理账号信息
+// pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
+//     let symbol_upper = symbol.to_uppercase();
+//     let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&res_data_str).unwrap();
+//     let balance_info_default = json!({"available":"0","coin": symbol_array[1],"frozen":"0","limitAvailable":"0","locked":"0","uTime":"0"});
+//     let balance_info = res_data_json.iter().find(|&item| item["coin"].as_str().unwrap() == symbol_array[1]).unwrap_or(&balance_info_default);
+//     let stocks_info_default = json!({"available":"0","coin": symbol_array[0],"frozen":"0","limitAvailable":"0","locked":"0","uTime":"0"});
+//     let stocks_info = res_data_json.iter().find(|&item| item["coin"].as_str().unwrap() == symbol_array[0]).unwrap_or(&stocks_info_default);
+//     format_account_info(balance_info.clone(), stocks_info.clone())
+// }
+//
+// pub fn format_account_info(balance_data: serde_json::Value, stocks_data: serde_json::Value) -> Account {
+//     let balance_coin = balance_data["coin"].as_str().unwrap().to_string().to_uppercase();
+//     let available_balance = Decimal::from_str(balance_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_balance = Decimal::from_str(balance_data["frozen"].as_str().unwrap()).unwrap();
+//     let balance = available_balance + frozen_balance;
+//
+//     let stocks_coin = stocks_data["coin"].as_str().unwrap().to_string().to_uppercase();
+//     let available_stocks = Decimal::from_str(stocks_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_stocks = Decimal::from_str(stocks_data["frozen"].as_str().unwrap()).unwrap();
+//     let stocks = available_stocks + frozen_stocks;
+//
+//     Account {
+//         coin: format!("{}_{}", stocks_coin, balance_coin),
+//         balance,
+//         available_balance,
+//         frozen_balance,
+//         stocks,
+//         available_stocks,
+//         frozen_stocks,
+//     }
+// }
+//
+// // 处理order信息
+// pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+//     let mut order_info = Vec::new();
+//     for item in res_data_json.iter() {
+//         order_info.push(format_order_item(item.clone(), ct_val));
+//     }
+//     trace!(?order_info);
+//     SpecialOrder {
+//         name: res_data.label,
+//         order: order_info,
+//     }
+// }
+//
+// // 处理订单信息
+// pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+//     let price = Decimal::from_str(order["price"].as_str().unwrap_or(order["priceAvg"].as_str().unwrap())).unwrap();
+//     let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
+//     let status = order["status"].as_str().unwrap_or("");
+//     let acc_base_volume = Decimal::from_str(order["accBaseVolume"].as_str().unwrap()).unwrap();
+//
+//     let avg_price = Decimal::from_str(order["priceAvg"].as_str().unwrap()).unwrap();
+//
+//     let amount = size * ct_val;
+//     let deal_amount = acc_base_volume * ct_val;
+//     let custom_status = if ["filled", "cancelled"].contains(&status) {
+//         "REMOVE".to_string()
+//     } else if ["init", "live", "new", "partially_filled"].contains(&status) {
+//         "NEW".to_string()
+//     } else {
+//         "NULL".to_string()
+//     };
+//     Order {
+//         id: order["orderId"].as_str().unwrap().to_string(),
+//         custom_id: order["clientOid"].as_str().unwrap().to_string(),
+//         price,
+//         amount,
+//         deal_amount,
+//         avg_price,
+//         status: custom_status,
+//         order_type: order["orderType"].as_str().unwrap().to_string(),
+//         trace_stack: TraceStack::new(0, Instant::now()).on_special("89 bitget_spot_handle".to_string()),
+//     }
+// }
+//
+// // 处理特殊深度数据
+// pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+//     HandleSwapInfo::handle_special_depth(ExchangeEnum::BitgetSpot, res_data)
+// }
+//
+// // 格式化深度信息
+// pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+//     let mut depth_items: Vec<MarketOrder> = vec![];
+//     for value in value.as_array().unwrap() {
+//         depth_items.push(MarketOrder {
+//             price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+//             amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+//         })
+//     }
+//     return depth_items;
+// }
+//
+// // 处理特殊Ticker信息
+// pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+//     format_special_ticker(res_data_json[0].clone(), res_data.label)
+// }
+//
+// pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+//     let bp = Decimal::from_str(data["bidPr"].as_str().unwrap()).unwrap();
+//     let bq = Decimal::from_str(data["bidSz"].as_str().unwrap()).unwrap();
+//     let ap = Decimal::from_str(data["askPr"].as_str().unwrap()).unwrap();
+//     let aq = Decimal::from_str(data["askSz"].as_str().unwrap()).unwrap();
+//     let mp = (bp + ap) * dec!(0.5);
+//     let t = Decimal::from_str(data["ts"].as_str().unwrap()).unwrap();
+//     let create_at = data["ts"].as_str().unwrap().parse::<i64>().unwrap() * 1000;
+//
+//     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+//     let depth_info = vec![bp, bq, ap, aq];
+//     SpecialDepth {
+//         name: label,
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at,
+//     }
+// }

+ 671 - 0
standard/src/bitget_swap.rs

@@ -0,0 +1,671 @@
+use std::collections::{BTreeMap};
+use exchanges::bitget_swap_rest::BitgetSwapRest;
+use std::io::{Error, ErrorKind};
+use tokio::sync::mpsc::Sender;
+use std::str::FromStr;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::{Decimal, MathematicalOps};
+use rust_decimal::prelude::ToPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::{json, Value};
+use tokio::spawn;
+use tokio::time::Instant;
+use tracing::{error, info};
+use global::trace_stack::TraceStack;
+use crate::exchange::ExchangeEnum;
+use crate::{Account, Market, Order, OrderCommand, Platform, Position, PositionModeEnum, Ticker, utils};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct BitgetSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: BitgetSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl BitgetSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BitgetSwap {
+        let market = Market::new();
+        let mut bitget_swap = BitgetSwap {
+            exchange: ExchangeEnum::BitgetSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: BitgetSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        bitget_swap.market = BitgetSwap::get_market(&mut bitget_swap).await.unwrap();
+        // 修改持仓模式
+        let mode_result = bitget_swap.set_dual_mode("", true).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("BitgetSwap:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("BitgetSwap:设置持仓模式失败!{:?}", error)
+            }
+        }
+        // 设置持仓杠杆
+        // let lever_rate_result = bitget_swap.set_dual_leverage("10").await;
+        // match lever_rate_result {
+        //     Ok(ok) => {
+        //         info!("BitgetSwap:设置持仓杠杆成功!{:?}", ok);
+        //     }
+        //     Err(error) => {
+        //         error!("BitgetSwap:设置持仓杠杆失败!{:?}", error)
+        //     }
+        // }
+
+        return bitget_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for BitgetSwap {
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+
+    fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::BitgetSwap }
+
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+
+    fn get_self_is_colo(&self) -> bool { self.is_colo }
+
+    fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+
+    fn get_self_market(&self) -> Market { self.market.clone() }
+
+    fn get_request_delays(&self) -> Vec<i64> {
+        // self.request.get_delays()
+        vec![]
+    }
+
+    fn get_request_avg_delay(&self) -> Decimal {
+        // self.request.get_avg_delay()
+        Decimal::ZERO
+    }
+
+    fn get_request_max_delay(&self) -> i64 { 0 }
+
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let res_data = self.request.get_server_time().await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let result = res_data_json["serverTime"].as_str().unwrap().to_string();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        let response = self.request.get_account_info().await;
+
+        if response.code == 200 {
+            for data in response.data.as_array().unwrap() {
+                if data["marginCoin"].as_str().unwrap() != "USDT" {
+                    continue
+                }
+
+                // 格式化account信息
+                let mut account = Account {
+                    coin: data["marginCoin"].to_string(),
+                    balance: Decimal::from_str(data["accountEquity"].as_str().unwrap()).unwrap(),
+                    available_balance: Decimal::from_str(data["available"].as_str().unwrap()).unwrap(),
+                    frozen_balance: Default::default(),
+                    stocks: Default::default(),
+                    available_stocks: Default::default(),
+                    frozen_stocks: Default::default(),
+                };
+                account.frozen_balance = account.balance - account.available_balance;
+
+                return Ok(account)
+            }
+
+            Err(Error::new(ErrorKind::NotFound, format!("bitget_usdt_swap 未能找到USDT账户:{}。", response.to_string())))
+        } else {
+            Err(Error::new(ErrorKind::Other, response.to_string()))
+        }
+    }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap get_spot_account:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "bitget_swap get_position:该交易所方法未实现".to_string())) }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        let params = json!({
+            "productType": "USDT-FUTURES",
+            "marginCoin": "USDT"
+        });
+        let response = self.request.get_all_position(params).await;
+        info!(?response);
+
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, format!("bitget_swap 获取仓位异常{:?}", response).to_string()))
+        }
+
+        // 正常处理持仓信息
+        let mut positions: Vec<Position> = vec![];
+        if response.data.is_null() {
+            return Ok(positions)
+        }
+
+        let positions_json = response.data.as_array().unwrap();
+        for position_json in positions_json {
+            let symbol = position_json["symbol"].as_str().unwrap().to_string();
+            let margin_level = Decimal::from_str(position_json["leverage"].as_str().unwrap()).unwrap();
+            let amount = Decimal::from_str(position_json["total"].as_str().unwrap()).unwrap();
+            let frozen_amount = Decimal::from_str(position_json["locked"].as_str().unwrap()).unwrap();
+            let price = Decimal::from_str(position_json["openPriceAvg"].as_str().unwrap()).unwrap();
+            let profit = Decimal::from_str(position_json["unrealizedPL"].as_str().unwrap()).unwrap();
+            let position_mode = match position_json["posMode"].as_str().unwrap() {
+                "hedge_mode" => {
+                    match position_json["holdSide"].as_str().unwrap() {
+                        "short" => {
+                            PositionModeEnum::Short
+                        }
+                        "long" => {
+                            PositionModeEnum::Long
+                        },
+                        _ => {
+                            panic!("bitget_usdt_swap: 未知的持仓模式与持仓方向: {}, {}",
+                                   position_json["posMode"].as_str().unwrap(), position_json["holdSide"].as_str().unwrap())
+                        }
+                    }
+                },
+                "one_way_mode" => {
+                    PositionModeEnum::Both
+                },
+                _ => {
+                    panic!("bitget_usdt_swap: 未知的持仓模式: {}", position_json["posMode"].as_str().unwrap())
+                }
+            };
+            let margin = Decimal::from_str(position_json["marginSize"].as_str().unwrap()).unwrap();
+
+            positions.push(Position {
+                symbol,
+                margin_level,
+                amount,
+                frozen_amount,
+                price,
+                profit,
+                position_mode,
+                margin,
+            });
+        }
+
+        Ok(positions)
+    }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        return self.get_ticker_symbol(self.symbol.clone()).await
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let res_data = self.request.get_tickers(symbol_format).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let ticker_info = res_data_json[0].clone();
+            let time = (Decimal::from_str(&*ticker_info["ts"].as_str().unwrap()).unwrap() / dec!(1000)).floor().to_i64().unwrap();
+            let result = Ticker {
+                time,
+                high: Decimal::from_str(ticker_info["high24h"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(ticker_info["low24h"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(ticker_info["askPr"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(ticker_info["bidPr"].as_str().unwrap()).unwrap(),
+                last: Decimal::from_str(ticker_info["lastPr"].as_str().unwrap()).unwrap(),
+                volume: Decimal::from_str(ticker_info["quoteVolume"].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> {
+        self.get_market_symbol(self.symbol.clone()).await
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let response = self.request.get_contracts(symbol_format.clone()).await;
+
+        if response.code == 200 {
+            let res_data_json = response.data.as_array().unwrap();
+            let market_info = res_data_json[0].clone();
+
+            info!(?market_info);
+            if !market_info["symbol"].as_str().unwrap().to_string().eq(&symbol_format) {
+                return Err(Error::new(ErrorKind::NotFound, format!("符号未找到:symbol={}, response={:?}", symbol_format, response))).unwrap();
+            }
+
+            let base_asset = market_info["baseCoin"].as_str().unwrap().to_string();
+            let quote_asset = market_info["quoteCoin"].as_str().unwrap().to_string();
+            let price_precision = Decimal::from_str(market_info["pricePlace"].as_str().unwrap()).unwrap();
+            let tick_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * price_precision);
+            let amount_precision = Decimal::from_str(market_info["volumePlace"].as_str().unwrap()).unwrap();
+            let amount_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * amount_precision);
+            let min_qty = Decimal::NEGATIVE_ONE;
+            let max_qty = Decimal::NEGATIVE_ONE;
+            // let ct_val = Decimal::from_str(&market_info["sizeMultiplier"].as_str().unwrap()).unwrap();
+            let ct_val = Decimal::ONE;
+
+            let result = Market {
+                symbol: format!("{}_{}", base_asset, quote_asset),
+                base_asset,
+                quote_asset,
+                tick_size,
+                amount_size,
+                price_precision,
+                amount_precision,
+                min_qty,
+                max_qty,
+                min_notional: min_qty,
+                max_notional: max_qty,
+                ct_val,
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, response.to_string()))
+        }
+    }
+
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+            "productType": "USDT-FUTURES",
+            "clientOid": custom_id,
+            "orderId": order_id
+        });
+
+        let ct_val = self.market.ct_val;
+        let response = self.request.get_order(params).await;
+        if response.code == 200 {
+            let res_data_json = response.data;
+            let result = format_order_item(res_data_json, ct_val);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, response.to_string()))
+        }
+    }
+
+    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap get_orders_list:该交易所方法未实现".to_string()))
+        // let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        // let ct_val = self.market.ct_val;
+        // let res_data = self.request.get_unfilled_orders(symbol_format.to_string(), "".to_string(), "".to_string(), "".to_string(), "100".to_string(), "".to_string()).await;
+        // if res_data.code == 200 {
+        //     let res_data_str = &res_data.data;
+        //     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).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 take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let ct_val = self.market.ct_val;
+
+        return self.take_order_symbol(self.symbol.clone(), ct_val, custom_id, origin_side, price, amount).await;
+    }
+
+    async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(symbol, "");
+        let final_size = amount / ct_val;
+        let mut params = json!({
+            "symbol": symbol_format,
+            "clientOid": custom_id,
+            "productType": "USDT-FUTURES",
+            "marginMode": "crossed",
+            "marginCoin": "USDT",
+            "size": final_size.to_string()
+        });
+        if price.eq(&Decimal::ZERO) {
+            params["orderType"] = json!("market");
+            params["force"] = json!("gtc");
+        } else {
+            params["price"] = json!(price.to_string());
+            params["orderType"] = json!("limit");
+            params["force"] = json!("gtc");
+        };
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+                params["tradeSide"] = json!("open");
+            }
+            "pd" => {
+                params["side"] = json!("buy");
+                params["tradeSide"] = json!("close");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+                params["tradeSide"] = json!("open");
+            }
+            "pk" => {
+                params["side"] = json!("sell");
+                params["tradeSide"] = json!("close");
+            }
+            _ => { panic!("bitget_usdt_swap 下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code != 200 {
+            return Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+
+        let res_data_json = res_data.data;
+        let result = Order {
+            id: res_data_json["orderId"].as_str().unwrap().to_string(),
+            custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+            price: Decimal::ZERO,
+            amount: Decimal::ZERO,
+            deal_amount: Decimal::ZERO,
+            avg_price: Decimal::ZERO,
+            status: "NEW".to_string(),
+            order_type: "".to_string(),
+            trace_stack: TraceStack::new(0, Instant::now()).on_special("328 bitget_swap".to_string()),
+        };
+        return Ok(result)
+    }
+
+    async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+            "productType": "USDT-FUTURES",
+            "clientOid": custom_id,
+            "orderId": order_id
+        });
+        let response = self.request.cancel_order(params).await;
+
+        // 取消失败,进行报错
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::NotFound, response.to_string()));
+        }
+
+        let res_data_json = response.data;
+        let result = Order {
+            id: res_data_json["orderId"].as_str().unwrap().to_string(),
+            custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+            price: Decimal::ZERO,
+            amount: Decimal::ZERO,
+            deal_amount: Decimal::ZERO,
+            avg_price: Decimal::ZERO,
+            status: "REMOVE".to_string(),
+            order_type: "".to_string(),
+            trace_stack: TraceStack::new(0, Instant::now()).on_special("443 bitget_swap".to_string()),
+        };
+        Ok(result)
+    }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap cancel_orders:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        let response = self.request.get_pending_orders().await;
+        info!(?response);
+        if response.code == 200 {
+            let mut result = vec![];
+            let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+
+            if !response.data["entrustedList"].is_null() {
+                let orders_res_data_json = response.data["entrustedList"].as_array().unwrap();
+                for order in orders_res_data_json {
+                    let order_id = order["orderId"].as_str().unwrap().to_string();
+                    let symbol = symbol_format.clone();
+                    let params = json!({
+                        "symbol": symbol,
+                        "productType": "USDT-FUTURES",
+                        "orderId": order_id,
+                    });
+                    let cancel_res_data = self.request.cancel_order(params).await;
+                    if cancel_res_data.code == 200 {
+                        let cancel_res_data_json = cancel_res_data.data;
+                        result.push(Order {
+                            id: cancel_res_data_json["orderId"].as_str().unwrap().to_string(),
+                            custom_id: cancel_res_data_json["clientOid"].as_str().unwrap().to_string(),
+                            price: Decimal::ZERO,
+                            amount: Decimal::ZERO,
+                            deal_amount: Decimal::ZERO,
+                            avg_price: Decimal::ZERO,
+                            status: "REMOVE".to_string(),
+                            order_type: "".to_string(),
+                            trace_stack: TraceStack::new(0, Instant::now()).on_special("457 bitget_swap".to_string()),
+                        });
+                    } else {
+                        return Err(Error::new(ErrorKind::Other, cancel_res_data.to_string()));
+                    }
+                }
+            }
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, response.to_string()))
+        }
+    }
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap take_stop_loss_order:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap cancel_stop_loss_order:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, is_dual_mode: bool) -> Result<String, Error> {
+        let pos_mode = if is_dual_mode {
+            "hedge_mode"
+        } else {
+            "one_way_mode"
+        };
+        let params = json!({
+            "productType": "USDT-FUTURES",
+            "posMode": pos_mode,
+        });
+        let response = self.request.set_position_mode(params).await;
+
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::Other, format!("设置持仓模式失败:{:?}", response).to_string()))
+        }
+
+        return Ok(response.data.to_string());
+    }
+
+    async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
+        let params = json!({
+            "symbol": "ETHUSDT",
+            "productType": "USDT-FUTURES",
+            "marginCoin": "USDT",
+            "leverage": leverage
+        });
+        let response = self.request.set_leverage(params).await;
+
+        if response.code != 200 {
+            return Err(Error::new(ErrorKind::Other, format!("设置杠杆失败:{:?}", response).to_string()))
+        }
+
+        return Ok(response.data.to_string());
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap set_auto_deposit_status:该交易所方法未实现".to_string()))
+    }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bitget_swap wallet_transfers:该交易所方法未实现".to_string()))
+        // let coin_format = coin.to_string().to_uppercase();
+        // let res_data = self.request.wallet_transfer(from.to_string(), to.to_string(), amount.to_string(), coin_format.clone(), "".to_string(), "".to_string()).await;
+        // if res_data.code == 200 {
+        //     let res_data_str = &res_data.data;
+        //     let result = res_data_str.clone();
+        //     Ok(result)
+        // } else {
+        //     Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        // }
+    }
+
+    async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
+        let mut handles = vec![];
+
+        // 下单指令
+        for item in order_command.limits_open.keys() {
+            let mut ts = trace_stack.clone();
+
+            let amount = Decimal::from_str(&*order_command.limits_open[item].get(0).unwrap().clone()).unwrap();
+            let side = order_command.limits_open[item].get(1).unwrap().clone();
+            let price = Decimal::from_str(&*order_command.limits_open[item].get(2).unwrap().clone()).unwrap();
+            let cid = order_command.limits_open[item].get(3).unwrap().clone();
+
+            //  order_name: [数量,方向,价格,c_id]
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                // TraceStack::show_delay(&ts.ins);
+                ts.on_before_send();
+                let result = self_clone.take_order(cid.as_str(), side.as_str(), price, amount).await;
+                ts.on_after_send();
+
+                match result {
+                    Ok(mut result) => {
+                        result.trace_stack = ts;
+
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        info!(?error);
+                        let mut err_order = Order::new();
+                        err_order.custom_id = cid.clone();
+                        err_order.status = "REMOVE".to_string();
+
+                        self_clone.order_sender.send(err_order).await.unwrap();
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(handles);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 撤销订单
+        let mut cancel_handlers = vec![];
+        for item in order_command.cancel.keys() {
+            let order_id = order_command.cancel[item].get(1).unwrap().clone();
+            let custom_id = order_command.cancel[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                let result = self_clone.cancel_order(&order_id, &custom_id).await;
+                match result {
+                    Ok(_) => {
+                        // result_sd.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        // 取消失败去查订单。
+                        let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+                        match query_rst {
+                            Ok(order) => {
+                                self_clone.order_sender.send(order).await.unwrap();
+                            }
+                            Err(err) => {
+                                error!("撤单失败,而且查单也失败了,bitget_swap,oid={}, cid={}, err={:?}。", order_id.clone(), custom_id.clone(), err);
+                            }
+                        }
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            cancel_handlers.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(cancel_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 检查订单指令
+        let mut check_handlers = vec![];
+        for item in order_command.check.keys() {
+            let order_id = order_command.check[item].get(1).unwrap().clone();
+            let custom_id = order_command.check[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                let result = self_clone.get_order_detail(order_id.as_str(), custom_id.as_str()).await;
+                match result {
+                    Ok(result) => {
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            check_handlers.push(handle)
+        }
+
+        let futures = FuturesUnordered::from_iter(check_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+    }
+}
+
+// pub fn format_account_info(balance_data: Value) -> Account {
+//     let balance_coin = balance_data["coin"].as_str().unwrap().to_string().to_uppercase();
+//     let available_balance = Decimal::from_str(balance_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_balance = Decimal::from_str(balance_data["frozen"].as_str().unwrap()).unwrap();
+//     let balance = available_balance + frozen_balance;
+//
+//     Account {
+//         coin: balance_coin,
+//         balance,
+//         available_balance,
+//         frozen_balance,
+//         stocks: Decimal::ZERO,
+//         available_stocks: Decimal::ZERO,
+//         frozen_stocks: Decimal::ZERO,
+//     }
+// }
+
+pub fn format_order_item(order: Value, ct_val: Decimal) -> Order {
+    let price = Decimal::from_str(order["price"].as_str().unwrap_or(order["priceAvg"].as_str().unwrap())).unwrap();
+    let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
+    let status = order["state"].as_str().unwrap();
+    let base_volume = Decimal::from_str(order["quoteVolume"].as_str().unwrap()).unwrap();
+    let avg_price = if order["priceAvg"].is_null() || order["priceAvg"].as_str().unwrap().is_empty() {
+        Decimal::ZERO
+    } else {
+        Decimal::from_str(order["priceAvg"].as_str().unwrap().to_string().as_str()).unwrap()
+    };
+
+    let amount = size * ct_val;
+    let deal_amount = base_volume * ct_val;
+    let custom_status = if ["filled", "cancelled"].contains(&status) {
+        "REMOVE".to_string()
+    } else if ["init", "live", "new", "partially_filled"].contains(&status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id: order["orderId"].as_str().unwrap().to_string(),
+        custom_id: order["clientOid"].as_str().unwrap().to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["orderType"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("700 bitget_swap".to_string()),
+    }
+}

+ 181 - 0
standard/src/bitget_swap_handle.rs

@@ -0,0 +1,181 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::Value;
+use tokio::time::Instant;
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialOrder};
+
+// 处理账号信息
+pub fn handle_account_info(response: &ResponseData, _symbol: &String) -> Account {
+    let mut rst = Account::new();
+
+    for data in response.data.as_array().unwrap() {
+        if data["marginCoin"].as_str().unwrap() != "USDT" {
+            continue
+        }
+
+        // 格式化account信息
+        let mut account = Account {
+            coin: data["marginCoin"].to_string(),
+            balance: Decimal::from_str(data["usdtEquity"].as_str().unwrap()).unwrap(),
+            available_balance: Decimal::from_str(data["available"].as_str().unwrap()).unwrap(),
+            frozen_balance: Decimal::from_str(data["frozen"].as_str().unwrap()).unwrap(),
+            stocks: Default::default(),
+            available_stocks: Default::default(),
+            frozen_stocks: Default::default(),
+        };
+        account.frozen_balance = account.balance - account.available_balance;
+
+        rst = account
+    }
+
+    return rst;
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+    let res_data_json = res_data.data.as_array().unwrap();
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), ct_val));
+    }
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+// 处理订单信息
+pub fn format_order_item(order: Value, ct_val: Decimal) -> Order {
+    let price = Decimal::from_str(order["price"].as_str().unwrap().to_string().as_str()).unwrap();
+    let size = Decimal::from_str(order["size"].as_str().unwrap().to_string().as_str()).unwrap();
+    let binding = order["status"].clone().as_str().unwrap().to_string();
+    let status = binding.as_str();
+    let acc_base_volume = Decimal::from_str(order["accBaseVolume"].as_str().unwrap().to_string().as_str()).unwrap();
+    let avg_price = if order["priceAvg"].is_null() || order["priceAvg"].as_str().unwrap().is_empty() {
+        Decimal::ZERO
+    } else {
+        Decimal::from_str(order["priceAvg"].as_str().unwrap().to_string().as_str()).unwrap()
+    };
+    let c_id = if order["clientOid"].is_null() {
+        ""
+    } else {
+        order["clientOid"].as_str().unwrap()
+    };
+
+    let amount = size * ct_val;
+    let deal_amount = acc_base_volume * ct_val;
+    let custom_status = if ["filled", "canceled"].contains(&status) {
+        "REMOVE".to_string()
+    } else if ["init", "live", "new", "partially_filled"].contains(&status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id: order["orderId"].as_str().unwrap().to_string(),
+        custom_id: c_id.to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["orderType"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("86 bitget_swap_handle".to_string()),
+    }
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
+    let res_data_json = res_data.data.as_array().unwrap();
+    res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect()
+}
+
+pub fn format_position_item(position_json: &Value, ct_val: &Decimal) -> Position {
+    let symbol = position_json["instId"].as_str().unwrap().to_string();
+    let margin_level = Decimal::from_i64(position_json["leverage"].as_i64().unwrap()).unwrap();
+    let amount = Decimal::from_str(position_json["total"].as_str().unwrap()).unwrap() * ct_val;
+    let frozen_amount = Decimal::from_str(position_json["frozen"].as_str().unwrap()).unwrap() * ct_val;
+    let price = Decimal::from_str(position_json["openPriceAvg"].as_str().unwrap()).unwrap();
+    let profit = Decimal::from_str(position_json["unrealizedPL"].as_str().unwrap()).unwrap();
+    let position_mode = match position_json["posMode"].as_str().unwrap() {
+        "hedge_mode" => {
+            match position_json["holdSide"].as_str().unwrap() {
+                "short" => {
+                    PositionModeEnum::Short
+                }
+                "long" => {
+                    PositionModeEnum::Long
+                },
+                _ => {
+                    panic!("bitget_usdt_swap: 未知的持仓模式与持仓方向: {}, {}",
+                           position_json["posMode"].as_str().unwrap(), position_json["holdSide"].as_str().unwrap())
+                }
+            }
+        },
+        "one_way_mode" => {
+            PositionModeEnum::Both
+        },
+        _ => {
+            panic!("bitget_usdt_swap: 未知的持仓模式: {}", position_json["posMode"].as_str().unwrap())
+        }
+    };
+    let margin = Decimal::from_str(position_json["marginSize"].as_str().unwrap()).unwrap();
+
+    Position {
+        symbol,
+        margin_level,
+        amount,
+        frozen_amount,
+        price,
+        profit,
+        position_mode,
+        margin,
+    }
+}
+
+// 处理特殊深度数据
+// pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+//     HandleSwapInfo::handle_special_depth(ExchangeEnum::BitgetSwap, res_data)
+// }
+
+// // 处理特殊Ticker信息
+// pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+//     format_special_ticker(res_data_json[0].clone(), res_data.label)
+// }
+//
+// pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+//     let bp = Decimal::from_str(data["bidPr"].as_str().unwrap()).unwrap();
+//     let bq = Decimal::from_str(data["bidSz"].as_str().unwrap()).unwrap();
+//     let ap = Decimal::from_str(data["askPr"].as_str().unwrap()).unwrap();
+//     let aq = Decimal::from_str(data["askSz"].as_str().unwrap()).unwrap();
+//     let mp = (bp + ap) * dec!(0.5);
+//     let t = Decimal::from_str(data["ts"].as_str().unwrap()).unwrap();
+//     let create_at = data["ts"].as_str().unwrap().parse::<i64>().unwrap() * 1000;
+//
+//     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+//     let depth_info = vec![bp, bq, ap, aq];
+//     SpecialDepth {
+//         name: label,
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at,
+//     }
+// }

+ 745 - 0
standard/src/bybit_swap.rs

@@ -0,0 +1,745 @@
+use std::collections::{BTreeMap};
+use std::io::{Error, ErrorKind};
+use std::str::FromStr;
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::Decimal;
+use serde_json::{from_value, json, Value};
+use rust_decimal::prelude::FromPrimitive;
+use serde::{Deserialize, Serialize};
+use tokio::time::Instant;
+use tracing::{error, trace};
+use exchanges::bybit_swap_rest::BybitSwapRest;
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, PositionModeEnum};
+use global::trace_stack::TraceStack;
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct SwapTicker {
+    symbol: String,
+    high_price24h: Decimal,
+    low_price24h: Decimal,
+    bid1_price: Decimal,
+    ask1_price: Decimal,
+    last_price: Decimal,
+    volume24h: Decimal
+}
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct BybitSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    symbol_uppercase: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: BybitSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl BybitSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BybitSwap {
+        let market = Market::new();
+        let mut bybit_swap = BybitSwap {
+            exchange: ExchangeEnum::BybitSwap,
+            symbol: symbol.to_uppercase(),
+            symbol_uppercase: symbol.replace("_", "").to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: BybitSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+
+        // 修改持仓模式
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mode_result = bybit_swap.set_dual_mode(symbol_array[1], true).await;
+        match mode_result {
+            Ok(_) => {
+                trace!("Bybit:设置持仓模式成功!")
+            }
+            Err(error) => {
+                error!("Bybit:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 获取市场信息
+        bybit_swap.market = BybitSwap::get_market(&mut bybit_swap).await.unwrap_or(bybit_swap.market);
+        return bybit_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for BybitSwap {
+    // 克隆方法
+    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 result = res_data.data["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_balance(symbol_array[1].parse().unwrap()).await;
+        if res_data.code == 200 {
+            let arr_infos: Vec<Value> = from_value(res_data.data["list"].clone()).unwrap();
+            if arr_infos.len() < 1usize{
+                return Err(Error::new(ErrorKind::NotFound, format!("{} 无账户信息", symbol_array[1])));
+            }
+            let coin_infos: Vec<Value> = from_value(arr_infos[0]["coin"].clone()).unwrap();
+            if coin_infos.len() < 1usize{
+               return Err(Error::new(ErrorKind::NotFound, format!("{} 无账户信息", symbol_array[1])));
+            }
+            let balance = Decimal::from_str(coin_infos[0]["equity"].as_str().unwrap()).unwrap();
+            let available_balance = Decimal::from_str(coin_infos[0]["walletBalance"].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, "bybit_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 获取持仓信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol = self.symbol_uppercase.clone();
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_positions(symbol, "".to_string()).await;
+        if res_data.code == 200 {
+            let result = res_data.data["list"].as_array().unwrap().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 ct_val = self.market.ct_val;
+        let res_data = self.request.get_positions("".to_string(), symbol_array[1].to_string().to_uppercase()).await;
+        if res_data.code == 200 {
+            let result = res_data.data["list"].as_array().unwrap().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_ticker(&mut self) -> Result<Ticker, Error> {
+        let symbol = self.symbol_uppercase.clone();
+        let res_data = self.request.get_tickers(symbol).await;
+        if res_data.code == 200 {
+            let list :Vec<SwapTicker> = from_value(res_data.data["list"].clone()).unwrap_or(Vec::new());
+
+            if list.len() < 1usize {
+                error!("bybit_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
+                return Err(Error::new(ErrorKind::Other, res_data.to_string()));
+            }
+            let value = list[0].clone();
+            Ok(Ticker{
+                time: chrono::Utc::now().timestamp_millis(),
+                high: value.high_price24h,
+                low: value.low_price24h,
+                sell: value.ask1_price,
+                buy: value.bid1_price,
+                last: value.last_price,
+                volume: value.volume24h
+            })
+        } 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.replace("_", "").to_uppercase();
+        let res_data = self.request.get_tickers(symbol_upper.clone()).await;
+        if res_data.code == 200 {
+            let list: Vec<SwapTicker> = from_value(res_data.data["list"].clone()).unwrap();
+            let ticker_info = list.iter().find(|&item| item.symbol == symbol_upper);
+
+            match ticker_info {
+                None => {
+                    error!("bybit_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: value.high_price24h,
+                        low: value.low_price24h,
+                        sell: value.ask1_price,
+                        buy: value.bid1_price,
+                        last: value.last_price,
+                        volume: value.volume24h
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol = self.symbol_uppercase.clone();
+        let res_data = self.request.get_instruments_info(symbol.clone()).await;
+        if res_data.code == 200 {
+            let arr_data: Vec<Value> = from_value(res_data.data["list"].clone()).unwrap();
+            let market_info = arr_data.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol);
+            match market_info {
+                None => {
+                    error!("bybit_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let base_coin = value["baseCoin"].as_str().unwrap();
+                    let quote_coin = value["quoteCoin"].as_str().unwrap();
+                    let name = format!("{}_{}",base_coin, quote_coin);
+                    let tick_size = Decimal::from_str(value["priceFilter"]["minPrice"].as_str().unwrap().trim()).unwrap();
+                    let min_qty = Decimal::from_str(value["lotSizeFilter"]["minOrderQty"].as_str().unwrap().trim()).unwrap();
+                    let max_qty = Decimal::from_str(value["lotSizeFilter"]["maxOrderQty"].as_str().unwrap().trim()).unwrap();
+                    let ct_val = Decimal::ONE;
+
+                    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,
+                        base_asset: base_coin.to_string(),
+                        quote_asset: quote_coin.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 = symbol.replace("_", "").to_uppercase();
+        let res_data = self.request.get_instruments_info(symbol.clone()).await;
+        if res_data.code == 200 {
+            let arr_data: Vec<Value> = from_value(res_data.data["list"].clone()).unwrap();
+            let market_info = arr_data.iter().find(|item| item["symbol"].as_str().unwrap() == symbol);
+            match market_info {
+                None => {
+                    error!("bybit_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let base_coin = value["baseCoin"].as_str().unwrap();
+                    let quote_coin = value["quoteCoin"].as_str().unwrap();
+                    let name = format!("{}_{}",base_coin, quote_coin);
+                    let tick_size = Decimal::from_str(value["priceFilter"]["minPrice"].as_str().unwrap().trim()).unwrap();
+                    let min_qty = Decimal::from_str(value["lotSizeFilter"]["minOrderQty"].as_str().unwrap().trim()).unwrap();
+                    let max_qty = Decimal::from_str(value["lotSizeFilter"]["maxOrderQty"].as_str().unwrap().trim()).unwrap();
+                    let ct_val = Decimal::ONE;
+
+                    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,
+                        base_asset: base_coin.to_string(),
+                        quote_asset: quote_coin.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 = self.symbol_uppercase.clone();
+        let ct_val = self.market.ct_val;
+        let id = if !custom_id.trim().eq("") { format!("t-{}", custom_id) } else { String::new() };
+        let res_data = self.request.get_order(symbol, order_id.parse().unwrap(), id).await;
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data["list"].clone();
+            if res_data_json.is_array() && res_data_json.as_array().unwrap().len() == 0 {
+                return Err(Error::new(ErrorKind::Other, "没有该订单!"));
+            }
+            let result = format_order_item(res_data_json.as_array().unwrap()[0].clone(), 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> {
+       Err(Error::new(ErrorKind::Other, "bybit获取订单列表暂未实现".to_string()))
+    }
+    // 下单接口
+    async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol = self.symbol_uppercase.clone();
+        let ct_val = self.market.ct_val;
+        let size = amount / ct_val;
+        let mut params = json!({
+            "orderLinkId": format!("t-{}", custom_id),
+            "symbol": symbol.to_string(),
+            "price": price.to_string(),
+            "category": "linear",
+            "orderType":"Limit",
+            "qty": json!(size),
+            // 0.單向持倉 1.買側雙向持倉 2.賣側雙向持倉
+            "positionIdx": json!(1),
+            "reduceOnly": json!(false)
+        });
+
+        if price.eq(&Decimal::ZERO) {
+            params["timeInForce"] = json!("IOC".to_string());
+        }
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("Buy");
+            }
+            "pd" => {
+                params["side"] = json!("Sell");
+                // 减仓
+                params["reduceOnly"] = json!(true);
+            }
+            "kk" => {
+                params["side"] = json!("Sell");
+                params["positionIdx"] = json!(2);
+            }
+            "pk" => {
+                params["side"] = json!("Buy");
+                // 减仓
+                params["reduceOnly"] = json!(true);
+                params["positionIdx"] = json!(2);
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == 200 {
+            let result = format_new_order_item(res_data.data, price, size);
+            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.replace("_", "").trim().to_uppercase();
+        let size = (amount / ct_val).floor();
+        let order_type = if price == Decimal::ZERO {
+            "Market"
+        } else {
+            "Limit"
+        };
+        let mut params = json!({
+            "orderLinkId": format!("t-{}", custom_id),
+            "symbol": symbol_upper,
+            "price": price.to_string(),
+            "category": "linear",
+            "orderType": order_type,
+            "qty": json!(size),
+            // 0.單向持倉 1.買側雙向持倉 2.賣側雙向持倉
+            "positionIdx": json!(1),
+            "reduceOnly": json!(false)
+        });
+
+        if price.eq(&Decimal::ZERO) {
+            params["timeInForce"] = json!("IOC".to_string());
+        }
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("Buy");
+            }
+            "pd" => {
+                params["side"] = json!("Sell");
+                params["positionIdx"] = json!(1);
+                // 减仓
+                params["reduceOnly"] = json!(true);
+            }
+            "kk" => {
+                params["side"] = json!("Sell");
+                params["positionIdx"] = json!(2);
+            }
+            "pk" => {
+                params["side"] = json!("Buy");
+                params["positionIdx"] = json!(2);
+                // 减仓
+                params["reduceOnly"] = json!(true);
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params.clone()).await;
+        if res_data.code == 200 {
+            let result = format_new_order_item(res_data.data, price, size);
+            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 = self.symbol_uppercase.clone();
+        let id = format!("t-{}", custom_id);
+        let res_data = self.request.cancel_order(symbol, String::from(order_id), id.clone()).await;
+        if res_data.code == 200 {
+            let result = format_cancel_order_item(res_data.data);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 批量撤销订单
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol = self.symbol_uppercase.clone();
+        let res_data = self.request.cancel_orders(symbol).await;
+        if res_data.code == 200 {
+            let res_arr: Vec<Value> = from_value(res_data.data).unwrap();
+            let result = res_arr.iter().map(|item| format_cancel_order_item(item.clone())).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 = self.symbol_uppercase.clone();
+        let res_data = self.request.cancel_orders(symbol).await;
+        if res_data.code == 200 {
+            let res_arr: Vec<Value> = from_value(res_data.data["list"].clone()).unwrap();
+            let result = res_arr.iter().map(|item| format_cancel_order_item(item.clone())).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bybit_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bybit_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 设置持仓模式
+    async fn set_dual_mode(&mut self, _coin: &str, is_dual_mode: bool) -> Result<String, Error> {
+        let coin_format = self.symbol_uppercase.clone();
+        let mut mod_num = 0;
+        if is_dual_mode {
+            mod_num = 3;
+        }
+        let res_data = self.request.set_position_mode(coin_format, mod_num).await;
+        if res_data.code == 200 {
+            Ok(res_data.data.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 = self.symbol_uppercase.clone();
+        let res_data = self.request.set_leverage(symbol, leverage.to_string()).await;
+        if res_data.code == 200 {
+            Ok(res_data.data.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)
+        // } else {
+        //     Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        // }
+        Err(Error::new(ErrorKind::Other, "暂未实现!"))
+    }
+
+    // 指令下单
+    async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
+        // 下单指令
+        let mut handles = vec![];
+        for item in order_command.limits_open.keys() {
+            let mut self_clone = self.clone();
+
+            let amount = Decimal::from_str(order_command.limits_open[item].get(0).unwrap_or(&"0".to_string())).unwrap();
+            let side = order_command.limits_open[item].get(1).unwrap().clone();
+            let price = Decimal::from_str(order_command.limits_open[item].get(2).unwrap_or(&"0".to_string())).unwrap();
+            let cid = order_command.limits_open[item].get(3).unwrap().clone();
+
+            let mut ts = trace_stack.clone();
+
+            let handle = tokio::spawn(async move {
+                ts.on_before_send();
+                // TraceStack::show_delay(&ts.ins);
+                let result = self_clone.take_order(cid.as_str(), side.as_str(), price, amount).await;
+                ts.on_after_send();
+                match result {
+                    Ok(mut result) => {
+                        // 记录此订单完成时间
+                        result.trace_stack = ts;
+
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        error!("bybit:下单失败:{:?}", error);
+
+                        let mut err_order = Order::new();
+                        err_order.custom_id = cid.clone();
+                        err_order.status = "REMOVE".to_string();
+
+                        self_clone.order_sender.send(err_order).await.unwrap();
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(handles);
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 撤销订单
+        let mut cancel_handles = vec![];
+        for item in order_command.cancel.keys() {
+            let mut self_clone = self.clone();
+
+            let order_id = order_command.cancel[item].get(1).unwrap().clone();
+            let custom_id = order_command.cancel[item].get(0).unwrap().clone();
+
+            let handle = tokio::spawn(async move {
+                let result = self_clone.cancel_order(&order_id, &custom_id).await;
+                match result {
+                    Ok(_) => {
+                        // result_sd.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        // 取消失败去查订单。
+                        let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+                        match query_rst {
+                            Ok(order) => {
+                                self_clone.order_sender.send(order).await.unwrap();
+                            }
+                            Err(_err) => {
+                                error!("bybit:撤单失败,而且查单也失败了,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+                            }
+                        }
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            cancel_handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(cancel_handles);
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 检查订单指令
+        let mut check_handles = vec![];
+        for item in order_command.check.keys() {
+            let mut self_clone = self.clone();
+
+            let order_id = order_command.check[item].get(1).unwrap().clone();
+            let custom_id = order_command.check[item].get(0).unwrap().clone();
+
+            let handle = tokio::spawn(async move {
+                let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+                match result {
+                    Ok(result) => {
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            check_handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(check_handles);
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+    }
+}
+
+pub fn format_position_item(position: &Value, ct_val: Decimal) -> Position {
+    let position_idx = position["positionIdx"].to_string();
+    let mut position_mode = match position_idx.as_str() {
+        "0" => PositionModeEnum::Both,
+        "1" => PositionModeEnum::Long,
+        "2" => PositionModeEnum::Short,
+        _ => {
+            error!("bybit_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("bybit_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size_str: String = from_value(position["size"].clone()).unwrap();
+    let size = Decimal::from_str(size_str.as_str()).unwrap();
+    let amount = size * ct_val;
+    let mut profit = Decimal::ZERO;
+    let profit_str = position["unrealisedPnl"].as_str().unwrap_or("0");
+    if profit_str != "" {
+        profit = Decimal::from_str(profit_str).unwrap();
+    }
+
+    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["symbol"].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["avgPrice"].as_str().unwrap()).unwrap(),
+        profit,
+        position_mode,
+        margin: Decimal::from_str(position["positionBalance"].as_str().unwrap()).unwrap(),
+    }
+}
+
+fn format_cancel_order_item(order: Value) -> Order {
+     Order {
+        id: format!("{}", order["orderId"].as_str().unwrap()),
+        custom_id: order["orderLinkId"].as_str().unwrap().replace("t-my-custom-id_", "").replace("t-", ""),
+        price: Decimal::ZERO,
+        amount: Decimal::ZERO,
+        deal_amount: Decimal::ZERO,
+        avg_price: Decimal::ZERO,
+        status: "REMOVE".to_string(),
+        order_type: "limit".to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("688 trace_stack".to_string())
+    }
+}
+
+fn format_new_order_item(order: Value, price: Decimal, amount: Decimal) -> Order {
+    Order {
+        id: format!("{}", order["orderId"].as_str().unwrap()),
+        custom_id: order["orderLinkId"].as_str().unwrap().replace("t-my-custom-id_", "").replace("t-", ""),
+        price,
+        amount,
+        deal_amount: Decimal::ZERO,
+        avg_price: price,
+        status: "NEW".to_string(),
+        order_type: "limit".to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("688 trace_stack".to_string())
+    }
+}
+
+pub fn format_order_item(order: Value, ct_val: Decimal) -> Order {
+    let status = order["orderStatus"].as_str().unwrap_or("");
+    let text = order["orderLinkId"].as_str().unwrap_or("");
+    let mut size = Decimal::ZERO;
+    let mut deal_amount = Decimal::ZERO;
+    let mut avg_price = Decimal::ZERO;
+
+    let right_str = order["cumExecQty"].to_string();
+    let size_str = order["qty"].to_string();
+
+    if !order.get("qty").is_some() {
+        size = Decimal::from_str(size_str.as_str()).unwrap();
+        let right_val = Decimal::from_str(order["cumExecValue"].as_str().unwrap()).unwrap();
+        let right = Decimal::from_str(right_str.as_str()).unwrap();
+        if right != Decimal::ZERO {
+            avg_price = right_val / right;
+        }
+        deal_amount = right * ct_val;
+    }
+
+    let amount = size * ct_val;
+    let custom_status = if status == "Filled" || status == "Cancelled" { "REMOVE".to_string() } else if status == "New" { "NEW".to_string() } else {
+        "NULL".to_string()
+    };
+    let rst_order = Order {
+        id: format!("{}", order["orderId"].as_str().unwrap()),
+        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,
+        status: custom_status,
+        order_type: "limit".to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("688 trace_stack".to_string()),
+    };
+    return rst_order;
+}

+ 172 - 0
standard/src/bybit_swap_handle.rs

@@ -0,0 +1,172 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use rust_decimal_macros::dec;
+use serde_json::{from_value, Value};
+use tokio::time::Instant;
+use tracing::{error};
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
+
+// 处理账号信息
+pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account {
+    format_account_info(res_data.data.as_array().unwrap().clone(), symbol)
+}
+
+pub fn format_account_info(data: Vec<Value>, symbol: &String) -> Account {
+    let account = data.iter().find(| &item | item["accountType"] == "UNIFIED");
+    match account {
+        None => {
+            error!("Bybit:格式化统一账户信息错误!\nformat_account_info: data={:?}", data);
+            panic!("Bybit:格式化统一账户信息错误!\nformat_account_info: data={:?}", data)
+        }
+        Some(val) =>{
+            let arr: Vec<Value> = from_value(val["coin"].clone()).unwrap();
+            let upper_str = symbol.to_uppercase();
+            let symbol_array: Vec<&str> = upper_str.split("_").collect();
+            let balance_info = arr.iter().find(|&item| item["coin"].as_str().unwrap() == symbol_array[1]);
+            match balance_info {
+                None => {
+                    error!("Bybit:格式化usdt余额信息错误!\nformat_account_info: data={:?}", balance_info);
+                    panic!("Bybit:格式化usdt余额信息错误!\nformat_account_info: data={:?}", balance_info)
+                }
+                Some(value) => {
+                    let balance = Decimal::from_str(&value["walletBalance"].as_str().unwrap().to_string()).unwrap();
+                    Account {
+                        coin: symbol_array[1].to_string(),
+                        balance,
+                        available_balance: Decimal::ZERO,
+                        frozen_balance: Decimal::ZERO,
+                        stocks: Decimal::ZERO,
+                        available_stocks: Decimal::ZERO,
+                        frozen_stocks: Decimal::ZERO,
+                    }
+                }
+            }
+        }
+    }
+}
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
+    res_data.data.as_array().unwrap().iter().map(|item| { format_position_item(item, ct_val) }).collect()
+}
+
+pub fn format_position_item(position: &Value, ct_val: &Decimal) -> Position {
+    let position_idx: String = position["positionIdx"].to_string();
+    let mut position_mode = match position_idx.as_str() {
+        "0" => PositionModeEnum::Both,
+        "1" => PositionModeEnum::Long,
+        "2" => PositionModeEnum::Short,
+        _ => {
+            error!("bybit_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("bybit_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let symbol_mapper =  position["symbol"].as_str().unwrap().to_string();
+    let currency = "USDT";
+    let coin = &symbol_mapper[..symbol_mapper.find(currency).unwrap_or(0)];
+    let size_str: String = from_value(position["size"].clone()).unwrap();
+    let size = Decimal::from_str(size_str.as_str()).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: format!{"{}_{}", coin, currency},
+        margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(position["entryPrice"].as_str().unwrap()).unwrap(),
+        profit: Decimal::from_str(position["unrealisedPnl"].as_str().unwrap()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(position["positionBalance"].as_str().unwrap()).unwrap(),
+    }
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+    let res_data_json: Vec<Value> = res_data.data.as_array().unwrap().clone();
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), ct_val));
+    };
+
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+pub fn format_order_item(order: Value, ct_val: Decimal) -> Order {
+    let status = order["orderStatus"].as_str().unwrap_or("");
+    let text = order["orderLinkId"].as_str().unwrap_or("");
+    let size = Decimal::from_str(order["qty"].as_str().unwrap()).unwrap();
+    let right = Decimal::from_str(order["cumExecQty"].as_str().unwrap()).unwrap();
+    let right_val = Decimal::from_str(order["cumExecValue"].as_str().unwrap()).unwrap();
+    let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+    let amount = size * ct_val;
+    let mut avg_price = Decimal::ZERO;
+    if right != Decimal::ZERO {
+        avg_price = right_val / right;
+    }
+    let deal_amount = right * ct_val;
+    let custom_status = if status == "Filled" || status == "Cancelled" { "REMOVE".to_string() } else if status == "New" { "NEW".to_string() } else {
+        "NULL".to_string()
+    };
+    let rst_order = Order {
+        id: format!("{}", order["orderId"].as_str().unwrap()),
+        custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: "limit".to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("120 bybit_handle".to_string()),
+    };
+
+    return rst_order;
+}
+
+// 处理特殊Ticket信息
+pub fn handle_ticker(res_data: &ResponseData) -> SpecialDepth {
+    let ap = Decimal::from_str(res_data.data["ask1Price"].as_str().unwrap()).unwrap();
+    let bp = Decimal::from_str(res_data.data["bid1Price"].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_str(res_data.data["ask1Size"].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_str(res_data.data["bid1Size"].as_str().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+
+    let t = Decimal::from_i64(res_data.data["ts"].as_i64().unwrap()).unwrap();
+    let create_at = t.to_i64().unwrap();
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at: 0 };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: res_data.label.clone(),
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+        create_at,
+    }
+}
+
+pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for val in value.as_array().unwrap() {
+        let arr = val.as_array().unwrap();
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(arr[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(arr[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}

+ 110 - 0
standard/src/exchange.rs

@@ -0,0 +1,110 @@
+use std::collections::{BTreeMap};
+use std::io::Error;
+use tokio::sync::mpsc::Sender;
+use crate::{Order, Platform};
+use crate::binance_swap::BinanceSwap;
+// use crate::binance_spot::BinanceSpot;
+// use crate::gate_spot::GateSpot;
+use crate::gate_swap::GateSwap;
+use crate::kucoin_swap::KucoinSwap;
+// use crate::bitget_spot::BitgetSpot;
+use crate::bybit_swap::BybitSwap;
+use crate::bitget_swap::BitgetSwap;
+// use crate::kucoin_spot::KucoinSpot;
+// use crate::okx_swap::OkxSwap;
+
+/// 交易所交易模式枚举
+/// - `BinanceSwap`: Binance交易所期货;
+/// - `BinanceSpot`: Binance交易所现货;
+/// - `GateSwap`: Gate交易所期货;
+/// - `GateSpot`: Gate交易所现货;
+/// - `KucoinSwap`: kucoin交易所期货;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ExchangeEnum {
+    BinanceSwap,
+    // BinanceSpot,
+    GateSwap,
+    // GateSpot,
+    KucoinSwap,
+    // KucoinSpot,
+    // OkxSwap,
+    // BitgetSpot,
+    BitgetSwap,
+    BybitSwap
+}
+
+/// 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::BinanceSwap => {
+                Box::new(BinanceSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            ExchangeEnum::GateSwap => {
+                Box::new(GateSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            // ExchangeEnum::GateSpot => {
+            //     Box::new(GateSpot::new(symbol, is_colo, params))
+            // }
+            ExchangeEnum::KucoinSwap => {
+                Box::new(KucoinSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            // ExchangeEnum::KucoinSpot =>{
+            //     Box::new(KucoinSpot::new(symbol, is_colo, params, order_sender, error_sender).await)
+            // }
+            // ExchangeEnum::OkxSwap => {
+            //     Box::new(OkxSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            // }
+            // ExchangeEnum::BitgetSpot => {
+            //     Box::new(BitgetSpot::new(symbol, is_colo, params, order_sender, error_sender).await)
+            // }
+            ExchangeEnum::BitgetSwap => {
+                Box::new(BitgetSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            ExchangeEnum::BybitSwap => {
+                Box::new(BybitSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+        }
+    }
+}
+
+
+

+ 113 - 0
standard/src/gate_spot.rs

@@ -0,0 +1,113 @@
+// use std::collections::BTreeMap;
+// use std::io::{Error, ErrorKind};
+// use async_trait::async_trait;
+// use rust_decimal::Decimal;
+// use rust_decimal_macros::dec;
+// use tracing::warn;
+// use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand};
+// use exchanges::gate_spot_rest::GateSpotRest;
+// use global::trace_stack::TraceStack;
+//
+// #[allow(dead_code)]
+// #[derive(Clone)]
+// pub struct GateSpot {
+//     exchange: ExchangeEnum,
+//     symbol: String,
+//     is_colo: bool,
+//     params: BTreeMap<String, String>,
+//     request: GateSpotRest,
+// }
+//
+// impl GateSpot {
+//     pub fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>) -> GateSpot {
+//         GateSpot {
+//             exchange: ExchangeEnum::GateSpot,
+//             symbol: symbol.to_uppercase(),
+//             is_colo,
+//             params: params.clone(),
+//             request: GateSpotRest::new(is_colo, params.clone()),
+//         }
+//     }
+// }
+//
+// #[async_trait]
+// impl Platform for GateSpot {
+//     // 克隆方法
+//     fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+//     // 获取交易所模式
+//     fn get_self_exchange(&self) -> ExchangeEnum {
+//         ExchangeEnum::GateSpot
+//     }
+//     // 获取交易对
+//     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()
+//     }
+//
+//     fn get_self_market(&self) -> Market {
+//         warn!("gate_spot:该交易所方法未实现");
+//         return Market::new();
+//     }
+//
+//     fn get_request_delays(&self) -> Vec<i64> {
+//         warn!("gate_spot:该交易所方法未实现");
+//         return vec![];
+//     }
+//
+//     fn get_request_avg_delay(&self) -> Decimal {
+//         warn!("gate_spot:该交易所方法未实现");
+//         return dec!(0);
+//     }
+//
+//     fn get_request_max_delay(&self) -> i64 {
+//         warn!("gate_spot:该交易所方法未实现");
+//         return 0;
+//     }
+//
+//     async fn get_server_time(&mut self) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_account(&mut self) -> Result<Account, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_position(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_ticker(&mut self) -> Result<Ticker, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_market(&mut self) -> Result<Market, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_market_symbol(&mut self, _symbol: String) -> Result<Market, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".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> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+//
+//     async fn command_order(&mut self, _order_command: OrderCommand, _trace_stack: TraceStack) { warn!("gate_spot:该交易所方法未实现"); }
+// }

+ 755 - 0
standard/src/gate_swap.rs

@@ -0,0 +1,755 @@
+use std::collections::{BTreeMap};
+use std::io::{Error, ErrorKind};
+use std::str::FromStr;
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use serde_json::{json, Value};
+use tokio::spawn;
+use tokio::time::Instant;
+use tracing::{error, info};
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, PositionModeEnum};
+use exchanges::gate_swap_rest::GateSwapRest;
+use global::trace_stack::TraceStack;
+
+#[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()))
+        }
+    }
+
+    // 指令下单
+    async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
+        let mut handles = vec![];
+
+        // 下单指令,limits_open里已经包含了limits_close
+        for item in order_command.limits_open.keys() {
+            let mut ts = trace_stack.clone();
+
+            let amount = Decimal::from_str(&*order_command.limits_open[item].get(0).unwrap().clone()).unwrap();
+            let side = order_command.limits_open[item].get(1).unwrap().clone();
+            let price = Decimal::from_str(&*order_command.limits_open[item].get(2).unwrap().clone()).unwrap();
+            let cid = order_command.limits_open[item].get(3).unwrap().clone();
+
+            //  order_name: [数量,方向,价格,c_id]
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                // TraceStack::show_delay(&ts.ins);
+                ts.on_before_send();
+                let result = self_clone.take_order(&cid, &side, price, amount).await;
+                ts.on_after_send();
+
+                match result {
+                    Ok(mut result) => {
+                        result.trace_stack = ts;
+
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        let mut err_order = Order::new();
+                        err_order.custom_id = cid.clone();
+                        err_order.status = "REMOVE".to_string();
+
+                        self_clone.order_sender.send(err_order).await.unwrap();
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(handles);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 撤销订单
+        let mut cancel_handlers = vec![];
+        for item in order_command.cancel.keys() {
+            let order_id = order_command.cancel[item].get(1).unwrap().clone();
+            let custom_id = order_command.cancel[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                let result = self_clone.cancel_order(&order_id, &custom_id).await;
+                match result {
+                    Ok(_) => {
+                        // result_sd.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        // 取消失败去查订单。
+                        let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+                        match query_rst {
+                            Ok(order) => {
+                                self_clone.order_sender.send(order).await.unwrap();
+                            }
+                            Err(_err) => {
+                                // error!("撤单失败,而且查单也失败了,gate_io_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+                                // panic!("撤单失败,而且查单也失败了,gate_io_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+                            }
+                        }
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            cancel_handlers.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(cancel_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 检查订单指令
+        let mut check_handlers = vec![];
+        for item in order_command.check.keys() {
+            let order_id = order_command.check[item].get(1).unwrap().clone();
+            let custom_id = order_command.check[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = spawn(async move {
+                let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+                match result {
+                    Ok(result) => {
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            check_handlers.push(handle)
+        }
+
+        let futures = FuturesUnordered::from_iter(check_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+    }
+}
+
+pub fn format_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(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("688 trace_stack".to_string()),
+    };
+    return rst_order;
+}

+ 153 - 0
standard/src/gate_swap_handle.rs

@@ -0,0 +1,153 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use tokio::time::Instant;
+use tracing::{error};
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
+
+// 处理账号信息
+pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account {
+    let res_data_json = res_data.data.as_array().unwrap();
+    format_account_info(res_data_json, symbol)
+}
+
+pub fn format_account_info(data: &Vec<Value>, symbol: &String) -> Account {
+    let symbol_upper = symbol.to_uppercase();
+    let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+    let balance_info = data.iter().find(|&item| item["text"].as_str().unwrap().contains(&symbol_upper));
+    match balance_info {
+        None => {
+            error!("Gate:格式化账号信息错误!\nformat_account_info: data={:?}", data);
+            panic!("Gate:格式化账号信息错误!\nformat_account_info: data={:?}", data)
+        }
+        Some(value) => {
+            let balance = Decimal::from_str(&value["balance"].as_f64().unwrap().to_string()).unwrap();
+            Account {
+                coin: symbol_array[1].to_string(),
+                balance,
+                available_balance: Decimal::ZERO,
+                frozen_balance: Decimal::ZERO,
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
+            }
+        }
+    }
+}
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
+    let res_data_json = res_data.data.as_array().unwrap();
+    res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect()
+}
+
+pub fn format_position_item(position: &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:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("Gate:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size = Decimal::from_str(&position["size"].as_f64().unwrap().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().to_string(),
+        margin_level: Decimal::from_str(&position["leverage"].as_f64().unwrap().to_string()).unwrap(),
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(&position["entry_price"].as_f64().unwrap().to_string()).unwrap(),
+        profit: Decimal::from_str(&position["realised_pnl"].as_f64().unwrap().to_string()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(&position["margin"].as_f64().unwrap().to_string()).unwrap(),
+    }
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+    let res_data_json = res_data.data.as_array().unwrap();
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), ct_val));
+    };
+
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+    let status = order["status"].as_str().unwrap_or("");
+    let text = order["text"].as_str().unwrap_or("");
+    let size = Decimal::from_f64(order["size"].as_f64().unwrap()).unwrap();
+    let left = Decimal::from_f64(order["left"].as_f64().unwrap()).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:格式化订单状态错误!\nformat_order_item:order={:?}", order);
+        panic!("Gate:格式化订单状态错误!\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_f64(order["price"].as_f64().unwrap()).unwrap(),
+        amount,
+        deal_amount,
+        avg_price: Decimal::from_f64(order["fill_price"].as_f64().unwrap()).unwrap(),
+        status: custom_status,
+        order_type: "limit".to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("120 gate_handle".to_string()),
+    };
+
+    return rst_order;
+}
+// 处理特殊Ticket信息
+pub fn handle_book_ticker(res_data: &ResponseData) -> SpecialDepth {
+    let bp = Decimal::from_str((*res_data).data["b"].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_f64((*res_data).data["B"].as_f64().unwrap()).unwrap();
+    let ap = Decimal::from_str((*res_data).data["a"].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_f64((*res_data).data["A"].as_f64().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+    let t = Decimal::from_u64((*res_data).data["u"].as_u64().unwrap()).unwrap();
+    let create_at = (*res_data).data["t"].as_i64().unwrap() * 1000;
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+    let depth_info = vec![bp, bq, ap, aq];
+
+    SpecialDepth {
+        name: (*res_data).label.clone(),
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+        create_at,
+    }
+}
+
+pub fn format_depth_items(value: &Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value["p"].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_f64(value["s"].as_f64().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}

+ 306 - 0
standard/src/handle_info.rs

@@ -0,0 +1,306 @@
+use std::cmp::Ordering;
+use std::str::FromStr;
+use rust_decimal::{Decimal};
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::{error, info};
+use exchanges::response_base::ResponseData;
+use global::public_params;
+use crate::exchange::ExchangeEnum;
+use crate::{binance_swap_handle, gate_swap_handle, bybit_swap_handle, bitget_swap_handle, kucoin_handle};
+use crate::{Account, MarketOrder, Position, SpecialDepth, SpecialOrder, SpecialTicker};
+
+#[allow(dead_code)]
+pub struct HandleSwapInfo;
+
+pub struct DepthParam {
+    pub depth_asks: Vec<MarketOrder>,
+    pub depth_bids: Vec<MarketOrder>,
+    pub t: Decimal,
+    pub create_at: i64
+}
+
+#[allow(dead_code)]
+impl HandleSwapInfo {
+    // 处理账号信息
+    pub fn handle_account_info(exchange: ExchangeEnum, res_data: &ResponseData, symbol: &String) -> Account {
+        match exchange {
+            // ExchangeEnum::BinanceSwap => {
+            //     error!("暂未提供此交易所方法!handle_account_info:{:?}", exchange);
+            //     panic!("暂未提供此交易所方法!handle_account_info:{:?}", exchange);
+            // }
+            ExchangeEnum::GateSwap => {
+                gate_swap_handle::handle_account_info(res_data, symbol)
+            }
+            // ExchangeEnum::KucoinSwap => {
+            //     kucoin_handle::handle_account_info(res_data, symbol)
+            // }
+            // ExchangeEnum::KucoinSpot => {
+            //     kucoin_spot_handle::handle_account_info(res_data, symbol)
+            // }
+            // ExchangeEnum::OkxSwap => {
+            //     okx_handle::handle_account_info(res_data, symbol)
+            // }
+            // ExchangeEnum::BitgetSpot => {
+            //     bitget_spot_handle::handle_account_info(res_data, symbol)
+            // },
+            ExchangeEnum::BitgetSwap => {
+                bitget_swap_handle::handle_account_info(res_data, symbol)
+            },
+            ExchangeEnum::BybitSwap => {
+                bybit_swap_handle::handle_account_info(res_data, symbol)
+            }
+            _ => {
+                error!("未找到该交易所!handle_account_info: {:?}",exchange);
+                panic!("未找到该交易所!handle_account_info: {:?}", exchange);
+            }
+        }
+    }
+    // 处理Ticker信息
+    pub fn handle_book_ticker(exchange: ExchangeEnum, res_data: &ResponseData) -> SpecialDepth {
+        match exchange {
+            // ExchangeEnum::BinanceSpot => {
+            //     binance_spot_handle::handle_special_ticker(res_data)
+            // }
+            ExchangeEnum::BinanceSwap => {
+                binance_swap_handle::handle_book_ticker(res_data)
+            }
+            ExchangeEnum::GateSwap => {
+                gate_swap_handle::handle_book_ticker(res_data)
+            }
+            ExchangeEnum::KucoinSwap => {
+                kucoin_handle::handle_book_ticker(res_data)
+            }
+            // ExchangeEnum::KucoinSpot => {
+            //     kucoin_spot_handle::handle_special_ticker(res_data)
+            // }
+            // ExchangeEnum::KucoinSpot => {
+            //     kucoin_spot_handle::handle_special_ticker(res_data)
+            // }
+            // ExchangeEnum::OkxSwap => {
+            //     okx_handle::handle_special_ticker(res_data)
+            // }
+            // ExchangeEnum::BitgetSpot => {
+            //     bitget_spot_handle::handle_special_ticker(res_data)
+            // },
+            ExchangeEnum::BitgetSwap => {
+                info!(?res_data);
+                panic!("BitgetSwap 85 未实现格式化");
+                // bitget_swap_handle::handle_special_ticker(res_data)
+            },
+            ExchangeEnum::BybitSwap => {
+                bybit_swap_handle::handle_ticker(res_data)
+            }
+        }
+    }
+    // 处理position信息
+    pub fn handle_position(exchange: ExchangeEnum, res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
+        match exchange {
+            ExchangeEnum::BinanceSwap => {
+                error!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+            }
+            ExchangeEnum::GateSwap => {
+                gate_swap_handle::handle_position(res_data, ct_val)
+            }
+            ExchangeEnum::KucoinSwap => {
+                kucoin_handle::handle_position(res_data, ct_val)
+            }
+            // ExchangeEnum::KucoinSpot => {
+            //     error!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+            //     panic!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+            // }
+            // ExchangeEnum::OkxSwap => {
+            //     okx_handle::handle_position(res_data, ct_val)
+            // }
+            // ExchangeEnum::BitgetSpot => {
+            //     error!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+            //     panic!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+            // },
+            ExchangeEnum::BitgetSwap => {
+                bitget_swap_handle::handle_position(res_data, ct_val)
+            },
+            ExchangeEnum::BybitSwap => {
+                bybit_swap_handle::handle_position(res_data, ct_val)
+            }
+        }
+    }
+    // 处理订单信息
+    pub fn handle_order(exchange: ExchangeEnum, res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+        match exchange {
+            ExchangeEnum::BinanceSwap => {
+                error!("暂未提供此交易所方法!handle_order:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_order:{:?}", exchange);
+            }
+            ExchangeEnum::GateSwap => {
+                gate_swap_handle::handle_order(res_data, ct_val)
+            }
+            ExchangeEnum::KucoinSwap => {
+                kucoin_handle::handle_order(res_data, ct_val)
+            }
+            // ExchangeEnum::KucoinSpot => {
+            //     kucoin_spot_handle::handle_order(res_data, ct_val)
+            // }
+            // ExchangeEnum::OkxSwap => {
+            //     okx_handle::handle_order(res_data, ct_val)
+            // }
+            // ExchangeEnum::BitgetSpot => {
+            //     bitget_spot_handle::handle_order(res_data, ct_val)
+            // },
+            ExchangeEnum::BitgetSwap => {
+                bitget_swap_handle::handle_order(res_data, ct_val)
+            },
+            ExchangeEnum::BybitSwap => {
+                bybit_swap_handle::handle_order(res_data, ct_val)
+            }
+        }
+    }
+
+    // 处理深度信息
+    pub fn handle_special_depth(exchange: ExchangeEnum, res_data: &ResponseData) -> SpecialDepth {
+        let label = res_data.label.clone();
+        // 格式化
+        let mut format_depth = format_depth(exchange, res_data);
+        // 运算、组装
+        make_special_depth(label, &mut format_depth.depth_asks, &mut format_depth.depth_bids, format_depth.t, format_depth.create_at)
+    }
+}
+
+
+pub fn make_special_depth(label: String, depth_asks: &mut Vec<MarketOrder>, depth_bids: &mut Vec<MarketOrder>, t: Decimal, create_at: i64) -> SpecialDepth {
+    depth_asks.sort_by(|a, b| a.price.partial_cmp(&b.price).unwrap_or(Ordering::Equal));
+    depth_bids.sort_by(|a, b| b.price.partial_cmp(&a.price).unwrap_or(Ordering::Equal));
+    // TODO 不排序的话,有4us可以省下来。
+    let mp = (depth_asks[0].price + depth_bids[0].price) * dec!(0.5);
+    let step = (public_params::EFF_RANGE * mp / Decimal::from_usize(public_params::LEVEL).unwrap()).round_dp(mp.scale());
+    let mut ap = Vec::new();
+    let mut bp = Vec::new();
+    let mut av: Vec<Decimal> = Vec::new();
+    let mut bv: Vec<Decimal> = Vec::new();
+    for i in 0..public_params::LEVEL {
+        let price = (depth_asks[0].price + step * Decimal::from_f64(i as f64).unwrap()).round_dp(depth_asks[0].price.scale());
+        ap.push(price);
+    }
+    for i in 0..public_params::LEVEL {
+        let price = (depth_bids[0].price - step * Decimal::from_f64(i as f64).unwrap()).round_dp(depth_bids[0].price.scale());
+        bp.push(price);
+    }
+    let mut ap_price_tag = depth_asks[0].price + step;
+    let mut ap_index = 0;
+    for item in depth_asks.iter() {
+        let price = item.price;
+        let amount = item.amount;
+        if av.get(ap_index).is_none() { av.push(Decimal::ZERO) };
+        if price < ap_price_tag {
+            av[ap_index] += amount;
+        } else {
+            ap_price_tag += step;
+            ap_index += 1;
+            if ap_index == public_params::LEVEL {
+                break;
+            }
+            av[ap_index] += amount
+        }
+    }
+
+    let mut bp_price_tag = depth_bids[0].price - step;
+    let mut bp_index = 0;
+    for item in depth_bids.iter() {
+        let price = item.price;
+        let amount = item.amount;
+        if bv.get(bp_index).is_none() { bv.push(Decimal::ZERO) };
+        if price > bp_price_tag {
+            bv[bp_index] += amount;
+        } else {
+            bp_price_tag -= step;
+            bp_index += 1;
+            if bp_index == public_params::LEVEL {
+                break;
+            }
+            bv[bp_index] += amount
+        }
+    }
+
+    let ticker_info = SpecialTicker { sell: depth_asks[0].price, buy: depth_bids[0].price, mid_price: mp, t, create_at };
+    let depth_info = bp.iter().cloned().chain(bv.iter().cloned()).chain(ap.iter().cloned()).chain(av.iter().cloned()).collect();
+    SpecialDepth {
+        name: label,
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+        create_at,
+    }
+}
+
+pub fn format_depth(exchange: ExchangeEnum, res_data: &ResponseData) -> DepthParam {
+    let depth_asks: Vec<MarketOrder>;
+    let depth_bids: Vec<MarketOrder>;
+    let t: Decimal;
+    let create_at: i64;
+    match exchange {
+        // ExchangeEnum::BinanceSpot => {
+        //     depth_asks = binance_swap_handle::format_depth_items(res_data_json["asks"].clone());
+        //     depth_bids = binance_swap_handle::format_depth_items(res_data_json["bids"].clone());
+        //     t = Decimal::from_str(&res_data_json["lastUpdateId"].to_string()).unwrap();
+        //     create_at = 0;
+        // }
+        ExchangeEnum::BinanceSwap => {
+            depth_asks = binance_swap_handle::format_depth_items(res_data.data["a"].clone());
+            depth_bids = binance_swap_handle::format_depth_items(res_data.data["b"].clone());
+            t = Decimal::from_str(&res_data.data["u"].to_string()).unwrap();
+            create_at = res_data.data["E"].as_i64().unwrap() * 1000;
+        }
+        ExchangeEnum::GateSwap => {
+            depth_asks = gate_swap_handle::format_depth_items(&res_data.data["asks"]);
+            depth_bids = gate_swap_handle::format_depth_items(&res_data.data["bids"]);
+            // todo! 有id可以取 保证与py一致
+            t = Decimal::from_str(&res_data.data["t"].to_string()).unwrap();
+            create_at = res_data.data["t"].as_i64().unwrap() * 1000;
+        }
+        ExchangeEnum::KucoinSwap => {
+            depth_asks = kucoin_handle::format_depth_items(res_data.data["asks"].clone());
+            depth_bids = kucoin_handle::format_depth_items(res_data.data["bids"].clone());
+            t = Decimal::from_str(&res_data.data["sequence"].to_string()).unwrap();
+            create_at = res_data.data["ts"].as_i64().unwrap() * 1000;
+        }
+        // ExchangeEnum::KucoinSpot => {
+        //     depth_asks = kucoin_spot_handle::format_depth_items(res_data_json["asks"].clone());
+        //     depth_bids = kucoin_spot_handle::format_depth_items(res_data_json["bids"].clone());
+        //     t = Decimal::from_str(&res_data_json["timestamp"].to_string()).unwrap();
+        //     create_at = res_data_json["timestamp"].as_i64().unwrap() * 1000;
+        // }
+        // ExchangeEnum::OkxSwap => {
+        //     depth_asks = okx_handle::format_depth_items(res_data_json[0]["asks"].clone());
+        //     depth_bids = okx_handle::format_depth_items(res_data_json[0]["bids"].clone());
+        //     t = Decimal::from_str(&res_data_json[0]["seqId"].to_string()).unwrap();
+        //     create_at = res_data_json[0]["ts"].as_str().unwrap().parse::<i64>().unwrap() * 1000;
+        // }
+        // ExchangeEnum::BitgetSpot => {
+        //     depth_asks = bitget_spot_handle::format_depth_items(res_data_json[0]["asks"].clone());
+        //     depth_bids = bitget_spot_handle::format_depth_items(res_data_json[0]["bids"].clone());
+        //     t = Decimal::from_str(res_data_json[0]["ts"].as_str().unwrap()).unwrap();
+        //     create_at = res_data_json[0]["ts"].as_str().unwrap().parse::<i64>().unwrap() * 1000;
+        // }
+        ExchangeEnum::BitgetSwap => {
+            depth_asks = bitget_swap_handle::format_depth_items(res_data.data[0]["asks"].clone());
+            depth_bids = bitget_swap_handle::format_depth_items(res_data.data[0]["bids"].clone());
+            t = Decimal::from_str(res_data.data[0]["ts"].as_str().unwrap()).unwrap();
+            create_at = res_data.data[0]["ts"].as_str().unwrap().parse::<i64>().unwrap() * 1000;
+        }
+        ExchangeEnum::BybitSwap => {
+            depth_asks = bybit_swap_handle::format_depth_items(res_data.data["a"].clone());
+            depth_bids = bybit_swap_handle::format_depth_items(res_data.data["b"].clone());
+            t = Decimal::from_i64(res_data.reach_time).unwrap();
+            create_at = res_data.reach_time * 1000;
+        }
+    }
+
+    DepthParam {
+        depth_asks,
+        depth_bids,
+        t,
+        create_at
+    }
+}
+
+

+ 155 - 0
standard/src/kucoin_handle.rs

@@ -0,0 +1,155 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use tokio::time::Instant;
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker, utils};
+use crate::exchange::ExchangeEnum;
+use crate::handle_info::HandleSwapInfo;
+
+// 处理账号信息
+pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account {
+    format_account_info(&res_data.data, symbol)
+}
+
+pub fn format_account_info(data: &Value, symbol: &String) -> Account {
+    let symbol_upper = symbol.to_uppercase();
+    let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+    let available_balance = Decimal::from_str(data["availableBalance"].as_str().unwrap()).unwrap();
+    let frozen_balance = Decimal::from_str(data["holdBalance"].as_str().unwrap()).unwrap();
+    let balance = available_balance + frozen_balance;
+    Account {
+        coin: symbol_array[1].to_string(),
+        balance,
+        available_balance,
+        frozen_balance,
+        stocks: Decimal::ZERO,
+        available_stocks: Decimal::ZERO,
+        frozen_stocks: Decimal::ZERO,
+    }
+}
+
+// 处理特殊Ticket信息
+pub fn handle_book_ticker(res_data: &ResponseData) -> SpecialDepth {
+    format_special_ticker(&res_data.data, &res_data.label)
+}
+
+pub fn format_special_ticker(data: &Value, label: &String) -> SpecialDepth {
+    let bp = Decimal::from_str(&data["bestBidPrice"].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_f64(data["bestBidSize"].as_f64().unwrap()).unwrap();
+    let ap = Decimal::from_str(&data["bestAskPrice"].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_f64(data["bestAskSize"].as_f64().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+    let t = Decimal::from_str(&data["sequence"].to_string()).unwrap();
+    let create_at = (data["ts"].as_f64().unwrap() / 1000.0).floor() as i64;
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: label.clone(),
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+        create_at,
+    }
+}
+
+pub fn format_position_item(position: &Value, ct_val: &Decimal) -> Position {
+    let symbol = position["symbol"].as_str().unwrap_or("");
+    let symbol_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, symbol);
+    let real_leverage = Decimal::from_f64(position["realLeverage"].as_f64().unwrap()).unwrap();
+    let currency = position["settleCurrency"].as_str().unwrap_or("");
+    let coin = &symbol_mapper[..symbol_mapper.find(currency).unwrap_or(0)];
+    let avg_entry_price = Decimal::from_f64(position["avgEntryPrice"].as_f64().unwrap()).unwrap();
+    let unrealised_pnl = Decimal::from_f64(position["unrealisedPnl"].as_f64().unwrap()).unwrap();
+    let pos_margin = Decimal::from_f64(position["posMargin"].as_f64().unwrap()).unwrap();
+
+    let current_qty = Decimal::from_f64(position["currentQty"].as_f64().unwrap()).unwrap();
+    let amount = current_qty * ct_val;
+    let position_mode = match amount {
+        amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+        amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+        _ => { PositionModeEnum::Both }
+    };
+    Position {
+        symbol: format!("{}_{}", coin, currency),
+        margin_level: real_leverage,
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: avg_entry_price,
+        profit: unrealised_pnl,
+        position_mode,
+        margin: pos_margin,
+    }
+}
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
+    let result = format_position_item(&res_data.data, ct_val);
+    return vec![result];
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+    let res_data_json = vec![res_data.data];
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item, ct_val));
+    }
+    SpecialOrder {
+        name: res_data.label.clone(),
+        order: order_info,
+    }
+}
+
+// ws处理
+pub fn format_order_item(order: &Value, ct_val: Decimal) -> Order {
+    let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+    let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
+    let status = order["status"].as_str().unwrap_or("");
+    let type_ = order["type"].as_str().unwrap_or("");
+    let filled_size = Decimal::from_str(order["filledSize"].as_str().unwrap()).unwrap();
+
+    let avg_price = price;
+
+    let amount = size * ct_val;
+    let deal_amount = filled_size * ct_val;
+    let custom_status;
+    if ["filled", "canceled"].contains(&type_) {
+        custom_status = "REMOVE".to_string();
+    } else if ["open"].contains(&status) {
+        custom_status = "NEW".to_string();
+    } else {
+        custom_status = "NULL".to_string();
+    }
+    Order {
+        id: order["orderId"].as_str().unwrap().to_string(),
+        custom_id: order["clientOid"].as_str().unwrap_or("").to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["type"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("136 kucoin_handle".to_string()),
+    }
+}
+
+// 处理特殊深度数据
+pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+    HandleSwapInfo::handle_special_depth(ExchangeEnum::KucoinSwap, &res_data)
+}
+
+pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_f64(value[1].as_f64().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}

+ 738 - 0
standard/src/kucoin_spot.rs

@@ -0,0 +1,738 @@
+// use std::collections::{BTreeMap, HashMap};
+// use std::io::{Error, ErrorKind};
+// use std::str::FromStr;
+// use tokio::sync::mpsc::Sender;
+// use async_trait::async_trait;
+// use futures::stream::FuturesUnordered;
+// use futures::TryStreamExt;
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::FromPrimitive;
+// use serde::{Deserialize, Serialize};
+// use serde_json::json;
+// use tokio::time::Instant;
+// use tracing::error;
+// use exchanges::kucoin_spot_rest::KucoinSpotRest;
+// use global::trace_stack::TraceStack;
+// use crate::exchange::ExchangeEnum;
+// use crate::{Account, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
+//
+// /// Kucoin交易所账户信息请求数据结构
+// /// 接口`"/api/v1/accounts"`;
+// ///
+// /// struct SpotAccount
+// /// - `id`: String, accountId
+// /// - `currency`: String, 币种
+// /// - `account_type`: String, 账户类型,资金(main)账户,现货交易(trade)账户,现货高频交易(trade_hf)账户,杠杆(margin)账户
+// /// - `balance`: Decimal, 资金总额
+// /// - `available`: Decimal, 可用余额
+// /// - `holds`: Decimal, 冻结金额
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SpotAccount {
+//     id: String,
+//     currency: String,
+//     account_type: String,
+//     balance: Decimal,
+//     available: Decimal,
+//     holds: Decimal,
+// }
+//
+// impl SpotAccount {
+//     fn new() -> SpotAccount {
+//         SpotAccount {
+//             id: "".to_string(),
+//             currency: "".to_string(),
+//             account_type: "".to_string(),
+//             balance: Default::default(),
+//             available: Default::default(),
+//             holds: Default::default(),
+//         }
+//     }
+// }
+//
+// /// Kucoin交易所Ticker信息请求数据结构
+// /// 接口`"/api/v1/market/orderbook/level1"`;
+// ///
+// /// struct SpotTicker
+// /// - `sequence`: String, 序列号
+// /// - `price`: Decimal, 最新成交价格
+// /// - `size`: Decimal, 最新成交数量
+// /// - `best_ask`: Decimal, 最佳卖一价
+// /// - `best_ask_size`: Decimal, 最佳卖一数量
+// /// - `best_bid`: Decimal, 最佳买一价
+// /// - `best_bid_size`: Decimal, 最佳买一数量
+// /// - `time`: i64, 时间戳
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SpotTicker {
+//     sequence: String,
+//     price: Decimal,
+//     size: Decimal,
+//     best_ask: Decimal,
+//     best_ask_size: Decimal,
+//     best_bid: Decimal,
+//     best_bid_size: Decimal,
+//     time: i64,
+// }
+//
+// /// Kucoin交易所Market信息请求数据结构
+// /// 接口`"/api/v2/symbols"`;
+// ///
+// /// struct SpotTicker
+// /// - `symbol: String, 交易对唯一标识码
+// /// - `name: String, 交易对名称
+// /// - `base_currency: String, 商品货币
+// /// - `quote_currency: String, 计价币种
+// /// - `fee_currency: String, 交易计算手续费的币种
+// /// - `market: String, 交易市场
+// /// - `base_min_size: Decimal, 下单时size的最小值
+// /// - `quote_min_size: Decimal, 下市价单,funds的最小值
+// /// - `base_max_size: Decimal, 下单,size的最大值
+// /// - `quote_max_size: Decimal, 下市价单,funds的最大值
+// /// - `base_increment: Decimal, 数量增量,下单的size必须为数量增量的正整数倍
+// /// - `quote_increment: Decimal, 市价单:资金增量,下单的funds必须为资金增量的正整数倍
+// /// - `price_increment: Decimal, 限价单:价格增量,下单的price必须为价格增量的正整数倍
+// /// - `price_limit_rate: Decimal, 价格保护阈值
+// /// - `min_funds: Option<Decimal>, 最小交易金额
+// /// - `is_margin_enabled: bool, 是否支持杠杆
+// /// - `enable_trading: bool, 是否可以用于交易
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SpotMarket {
+//     symbol: String,
+//     name: String,
+//     base_currency: String,
+//     quote_currency: String,
+//     fee_currency: String,
+//     market: String,
+//     base_min_size: Decimal,
+//     quote_min_size: Decimal,
+//     base_max_size: Decimal,
+//     quote_max_size: Decimal,
+//     base_increment: Decimal,
+//     quote_increment: Decimal,
+//     price_increment: Decimal,
+//     price_limit_rate: Decimal,
+//     min_funds: Option<Decimal>,
+//     is_margin_enabled: bool,
+//     enable_trading: bool,
+// }
+//
+// /// Kucoin交易所Order信息请求数据结构
+// /// 接口`"/api/v1/orders/{orderId}"`;
+// ///
+// /// struct SpotOrder
+// /// - `id`: String,
+// /// - `symbol`: String,
+// /// - `op_type`: String,
+// /// - `order_type`: String,
+// /// - `side`: String,
+// /// - `price`: Decimal,
+// /// - `size`: Decimal,
+// /// - `funds`: Decimal,
+// /// - `deal_funds`: Decimal,
+// /// - `deal_size`: Decimal,
+// /// - `fee`: Decimal,
+// /// - `fee_currency`: String,
+// /// - `stp`: String,
+// /// - `stop`: String,
+// /// - `stop_triggered`: bool,
+// /// - `stop_price`: Decimal,
+// /// - `time_in_force`: String,
+// /// - `post_only`: bool,
+// /// - `hidden`: bool,
+// /// - `iceberg`: bool,
+// /// - `visible_size`: Decimal,
+// /// - `cancel_after`: i64,
+// /// - `channel`: String,
+// /// - `client_oid`: String,
+// /// - `remark`: String,
+// /// - `tags`: String,
+// /// - `is_active`: bool,
+// /// - `cancel_exist`: bool,
+// /// - `created_at`: i64,
+// /// - `trade_type`: String,
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SpotOrder {
+//     id: String,
+//     symbol: String,
+//     op_type: String,
+//     #[serde(rename = "type")]
+//     order_type: String,
+//     side: String,
+//     price: Decimal,
+//     size: Decimal,
+//     funds: Decimal,
+//     deal_funds: Decimal,
+//     deal_size: Decimal,
+//     fee: Decimal,
+//     fee_currency: String,
+//     stp: String,
+//     stop: String,
+//     stop_triggered: bool,
+//     stop_price: Decimal,
+//     time_in_force: String,
+//     post_only: bool,
+//     hidden: bool,
+//     iceberg: bool,
+//     visible_size: Decimal,
+//     cancel_after: i64,
+//     channel: String,
+//     client_oid: String,
+//     remark: String,
+//     tags: String,
+//     is_active: bool,
+//     cancel_exist: bool,
+//     created_at: i64,
+//     trade_type: String,
+// }
+//
+// #[allow(dead_code)]
+// #[derive(Clone)]
+// pub struct KucoinSpot {
+//     exchange: ExchangeEnum,
+//     symbol: String,
+//     is_colo: bool,
+//     params: BTreeMap<String, String>,
+//     request: KucoinSpotRest,
+//     market: Market,
+//     order_sender: Sender<Order>,
+//     error_sender: Sender<Error>,
+// }
+//
+// impl KucoinSpot {
+//     pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> KucoinSpot {
+//         let market = Market::new();
+//         let mut kucoin_spot = KucoinSpot {
+//             exchange: ExchangeEnum::KucoinSpot,
+//             symbol: symbol.to_uppercase(),
+//             is_colo,
+//             params: params.clone(),
+//             request: KucoinSpotRest::new(is_colo, params.clone()),
+//             market,
+//             order_sender,
+//             error_sender,
+//         };
+//         kucoin_spot.market = KucoinSpot::get_market(&mut kucoin_spot).await.unwrap_or(kucoin_spot.market);
+//
+//         return kucoin_spot;
+//     }
+// }
+//
+// #[async_trait]
+// impl Platform for KucoinSpot {
+//     // 克隆方法
+//     fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+//     fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::KucoinSpot }
+//     // 获取交易对
+//     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_str = &res_data.data;
+//             let result = res_data_str.clone();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_account(&mut self) -> Result<Account, Error> {
+//         let coin_array: Vec<&str> = self.symbol.split("_").collect();
+//         let res_data = self.request.get_accounts(coin_array[1].to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let balance_info_list: Vec<SpotAccount> = serde_json::from_str(res_data_str).unwrap();
+//             let mut balance_info_default = SpotAccount::new();
+//             balance_info_default.currency = coin_array[1].to_string();
+//             let balance_info = balance_info_list.iter().find(|&item| item.currency == coin_array[1].to_string()).unwrap_or(&balance_info_default);
+//             let mut stocks_info_default = SpotAccount::new();
+//             stocks_info_default.currency = coin_array[0].to_string();
+//             let stocks_info = balance_info_list.iter().find(|&item| item.currency == coin_array[0].to_string()).unwrap_or(&balance_info_default);
+//             let result = Account {
+//                 coin: format!("{}_{}", balance_info.currency, stocks_info.currency),
+//                 balance: balance_info.available + balance_info.holds,
+//                 available_balance: balance_info.available,
+//                 frozen_balance: balance_info.holds,
+//                 stocks: stocks_info.available + stocks_info.holds,
+//                 available_stocks: stocks_info.available,
+//                 frozen_stocks: stocks_info.holds,
+//             };
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+//         let res_data = self.request.get_accounts("".to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let balance_info_list: Vec<SpotAccount> = serde_json::from_str(res_data_str).unwrap();
+//             let result = balance_info_list.iter().map(|item| {
+//                 Account {
+//                     coin: item.currency.to_string(),
+//                     balance: item.available + item.holds,
+//                     available_balance: item.available,
+//                     frozen_balance: item.holds,
+//                     stocks: Decimal::ZERO,
+//                     available_stocks: Decimal::ZERO,
+//                     frozen_stocks: Decimal::ZERO,
+//                 }
+//             }).collect();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "kucoin_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "kucoin_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+//         let res_data = self.request.get_level1(symbol_format).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let ticker_info: SpotTicker = serde_json::from_str(res_data_str).unwrap();
+//             let result = Ticker {
+//                 time: ticker_info.time,
+//                 high: ticker_info.best_ask,
+//                 low: ticker_info.best_bid,
+//                 sell: ticker_info.best_ask,
+//                 buy: ticker_info.best_bid,
+//                 last: ticker_info.price,
+//                 volume: ticker_info.size,
+//             };
+//             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_format = utils::format_symbol(symbol.clone(), "-");
+//         let res_data = self.request.get_level1(symbol_format).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let ticker_info: SpotTicker = serde_json::from_str(res_data_str).unwrap();
+//             let result = Ticker {
+//                 time: ticker_info.time,
+//                 high: ticker_info.best_ask,
+//                 low: ticker_info.best_bid,
+//                 sell: ticker_info.best_ask,
+//                 buy: ticker_info.best_bid,
+//                 last: ticker_info.price,
+//                 volume: ticker_info.size,
+//             };
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_market(&mut self) -> Result<Market, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+//         let res_data = self.request.get_symbols().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let market_info_list: Vec<SpotMarket> = serde_json::from_str(res_data_str).unwrap();
+//             let market_info = market_info_list.iter().find(|&item| item.symbol == symbol_format).unwrap();
+//             let result = Market {
+//                 symbol: market_info.symbol.replace("-", "_"),
+//                 base_asset: market_info.base_currency.clone(),
+//                 quote_asset: market_info.quote_currency.clone(),
+//                 tick_size: market_info.price_increment,
+//                 amount_size: market_info.base_increment,
+//                 price_precision: Decimal::from_u32(market_info.price_increment.scale()).unwrap(),
+//                 amount_precision: Decimal::from_u32(market_info.base_increment.scale()).unwrap(),
+//                 min_qty: market_info.base_min_size,
+//                 max_qty: market_info.base_max_size,
+//                 min_notional: market_info.price_increment * market_info.base_min_size,
+//                 max_notional: market_info.price_increment * market_info.base_max_size,
+//                 ct_val: Decimal::ONE,
+//             };
+//             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_format = utils::format_symbol(symbol.clone(), "-");
+//         let res_data = self.request.get_symbols().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let market_info_list: Vec<SpotMarket> = serde_json::from_str(res_data_str).unwrap();
+//             let market_info = market_info_list.iter().find(|&item| item.symbol == symbol_format).unwrap();
+//             let result = Market {
+//                 symbol: market_info.symbol.replace("-", "_"),
+//                 base_asset: market_info.base_currency.clone(),
+//                 quote_asset: market_info.quote_currency.clone(),
+//                 tick_size: market_info.price_increment,
+//                 amount_size: market_info.base_increment,
+//                 price_precision: Decimal::from_u32(market_info.price_increment.scale()).unwrap(),
+//                 amount_precision: Decimal::from_u32(market_info.base_increment.scale()).unwrap(),
+//                 min_qty: market_info.base_min_size,
+//                 max_qty: market_info.base_max_size,
+//                 min_notional: market_info.price_increment * market_info.base_min_size,
+//                 max_notional: market_info.price_increment * market_info.base_max_size,
+//                 ct_val: Decimal::ONE,
+//             };
+//             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 res_data = if order_id != "" { self.request.get_order_by_order_id(order_id.to_string()).await } else { self.request.get_order_by_client_id(custom_id.to_string()).await };
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let result = format_order_item(res_data_str.clone());
+//             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 res_data = self.request.get_order().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let order_info_list: Vec<String> = serde_json::from_str(res_data_str).unwrap();
+//             let result = order_info_list.iter().map(|item| format_order_item(item.clone())).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_format = utils::format_symbol(self.symbol.clone(), "-");
+//         let mut params = json!({
+//             "symbol": symbol_format.to_string(),
+//             "clientOid": custom_id,
+//             "price": price.to_string(),
+//             "size": amount.to_string()
+//         });
+//         if price.eq(&Decimal::ZERO) {
+//             params["type"] = json!("market");
+//         } else {
+//             params["type"] = json!("limit");
+//         };
+//         match origin_side {
+//             "kd" => {
+//                 params["side"] = json!("buy");
+//             }
+//             "pd" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "kk" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "pk" => {
+//                 params["side"] = json!("buy");
+//             }
+//             _ => { error!("下单参数错误"); }
+//         };
+//         let res_data = self.request.spot_order(params).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = Order {
+//                 id: res_data_json["orderId"].as_str().unwrap().to_string(),
+//                 custom_id: custom_id.to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "NEW".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("550 kucoin_spot".to_string()),
+//             };
+//             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_format = utils::format_symbol(symbol.clone(), "-");
+//         let mut params = json!({
+//             "symbol": symbol_format.to_string(),
+//             "clientOid": custom_id,
+//             "price": price.to_string(),
+//             "size": amount * ct_val,
+//         });
+//         if price.eq(&Decimal::ZERO) {
+//             params["type"] = json!("market");
+//         } else {
+//             params["type"] = json!("limit");
+//         };
+//         match origin_side {
+//             "kd" => {
+//                 params["side"] = json!("buy");
+//             }
+//             "pd" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "kk" => {
+//                 params["side"] = json!("sell");
+//             }
+//             "pk" => {
+//                 params["side"] = json!("buy");
+//             }
+//             _ => { error!("下单参数错误"); }
+//         };
+//         let res_data = self.request.spot_order(params).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = Order {
+//                 id: res_data_json["orderId"].as_str().unwrap().to_string(),
+//                 custom_id: custom_id.to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "NEW".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("599 kucoin_spot".to_string()),
+//             };
+//             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 res_data = if order_id != "" { self.request.cancel_order_by_order_id(order_id.to_string()).await } else { self.request.cancel_order_by_client_id(custom_id.to_string()).await };
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let order_info: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let id = if order_id != "" { order_info["cancelledOrderIds"][0].as_str().unwrap().to_string() } else { order_info["cancelledOrderId"].as_str().unwrap().to_string() };
+//             let custom_id = if order_id != "" { "".to_string() } else { order_info["clientOid"].as_str().unwrap().to_string() };
+//             let result = Order {
+//                 id,
+//                 custom_id,
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "REMOVE".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("623 kucoin_spot".to_string()),
+//             };
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+//         let res_data = self.request.cancel_order_all(symbol_format).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let order_info: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let id_list = order_info["cancelledOrderIds"].as_array().unwrap();
+//             let result = id_list.iter().map(|item| Order {
+//                 id: item["id"].as_str().unwrap().to_string(),
+//                 custom_id: "".to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "REMOVE".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("647 kucoin_spot".to_string()),
+//             }).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 res_data = self.request.cancel_order_all("".to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let order_info: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let id_list = order_info["cancelledOrderIds"].as_array().unwrap();
+//             let result = id_list.iter().map(|item| Order {
+//                 id: item["id"].as_str().unwrap().to_string(),
+//                 custom_id: "".to_string(),
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "REMOVE".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("670 kucoin_spot".to_string()),
+//             }).collect();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "kucoin_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "kucoin_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "kucoin_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "kucoin_spot:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
+//         let mut handles = vec![];
+//         // 撤销订单
+//         let cancel = order_command.cancel;
+//         for item in cancel.keys() {
+//             let mut self_clone = self.clone();
+//             let cancel_clone = cancel.clone();
+//             let item_clone = item.clone();
+//             let order_id = cancel_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+//             let custom_id = cancel_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let handle = tokio::spawn(async move {
+//                 let result = self_clone.cancel_order(&order_id, &custom_id).await;
+//                 match result {
+//                     Ok(_) => {
+//                         // result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         // 取消失败去查订单。
+//                         let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+//                         match query_rst {
+//                             Ok(order) => {
+//                                 result_sd.send(order).await.unwrap();
+//                             }
+//                             Err(_query_err) => {
+//                                 // error!(?_query_err);
+//                                 // error!("撤单失败,而且查单也失败了,bitget_spot,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+//                             }
+//                         }
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//         // 下单指令
+//         let mut limits = HashMap::new();
+//         limits.extend(order_command.limits_open);
+//         limits.extend(order_command.limits_close);
+//         for item in limits.keys() {
+//             let mut self_clone = self.clone();
+//             let limits_clone = limits.clone();
+//             let item_clone = item.clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let ts = trace_stack.clone();
+//
+//             let handle = tokio::spawn(async move {
+//                 let value = limits_clone[&item_clone].clone();
+//                 let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
+//                 let side = value.get(1).unwrap();
+//                 let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
+//                 let cid = value.get(3).unwrap();
+//
+//                 //  order_name: [数量,方向,价格,c_id]
+//                 let result = self_clone.take_order(cid, side, price, amount).await;
+//                 match result {
+//                     Ok(mut result) => {
+//                         // 记录此订单完成时间
+//                         // ts.on_after_send();
+//                         result.trace_stack = ts;
+//
+//                         result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         let mut err_order = Order::new();
+//                         err_order.custom_id = cid.clone();
+//                         err_order.status = "REMOVE".to_string();
+//
+//                         result_sd.send(err_order).await.unwrap();
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//         // 检查订单指令
+//         let check = order_command.check;
+//         for item in check.keys() {
+//             let mut self_clone = self.clone();
+//             let check_clone = check.clone();
+//             let item_clone = item.clone();
+//             let order_id = check_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+//             let custom_id = check_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let handle = tokio::spawn(async move {
+//                 let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+//                 match result {
+//                     Ok(result) => {
+//                         result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//
+//         let futures = FuturesUnordered::from_iter(handles);
+//         let _: Result<Vec<_>, _> = futures.try_collect().await;
+//     }
+// }
+//
+// pub fn format_order_item(data: String) -> Order {
+//     let order_info: SpotOrder = serde_json::from_str(&data).unwrap();
+//     Order {
+//         id: order_info.id,
+//         custom_id: order_info.client_oid,
+//         price: order_info.price,
+//         amount: order_info.size,
+//         deal_amount: order_info.deal_size,
+//         avg_price: order_info.deal_funds / order_info.deal_size,
+//         status: if order_info.is_active { "NEW".to_string() } else { "REMOVE".to_string() },
+//         order_type: order_info.order_type,
+//         trace_stack: TraceStack::new(0, Instant::now()).on_special("811 kucoin_spot".to_string()),
+//     }
+// }

+ 136 - 0
standard/src/kucoin_spot_handle.rs

@@ -0,0 +1,136 @@
+// use std::str::FromStr;
+// use rust_decimal::Decimal;
+// use rust_decimal_macros::dec;
+// use serde_json::json;
+// use tokio::time::Instant;
+// use tracing::trace;
+// use exchanges::response_base::ResponseData;
+// use global::trace_stack::TraceStack;
+// use crate::{Account, MarketOrder, Order, SpecialDepth, SpecialOrder, SpecialTicker};
+// use crate::exchange::ExchangeEnum;
+// use crate::handle_info::HandleSwapInfo;
+//
+// // 处理账号信息
+// pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
+//     let symbol_upper = symbol.to_uppercase();
+//     let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&res_data_str).unwrap();
+//     let balance_info_default = json!({"available":"0","currency": symbol_array[1],"hold":"0"});
+//     let balance_info = res_data_json.iter().find(|&item| item["currency"].as_str().unwrap() == symbol_array[1]).unwrap_or(&balance_info_default);
+//     let stocks_info_default = json!({"available":"0","currency": symbol_array[0],"hold":"0"});
+//     let stocks_info = res_data_json.iter().find(|&item| item["currency"].as_str().unwrap() == symbol_array[0]).unwrap_or(&stocks_info_default);
+//     format_account_info(balance_info.clone(), stocks_info.clone())
+// }
+//
+// pub fn format_account_info(balance_data: serde_json::Value, stocks_data: serde_json::Value) -> Account {
+//     let balance_coin = balance_data["currency"].as_str().unwrap().to_string().to_uppercase();
+//     let available_balance = Decimal::from_str(balance_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_balance = Decimal::from_str(balance_data["hold"].as_str().unwrap()).unwrap();
+//     let balance = available_balance + frozen_balance;
+//
+//     let stocks_coin = stocks_data["currency"].as_str().unwrap().to_string().to_uppercase();
+//     let available_stocks = Decimal::from_str(stocks_data["available"].as_str().unwrap()).unwrap();
+//     let frozen_stocks = Decimal::from_str(stocks_data["hold"].as_str().unwrap()).unwrap();
+//     let stocks = available_stocks + frozen_stocks;
+//
+//     Account {
+//         coin: format!("{}_{}", stocks_coin, balance_coin),
+//         balance,
+//         available_balance,
+//         frozen_balance,
+//         stocks,
+//         available_stocks,
+//         frozen_stocks,
+//     }
+// }
+//
+// // 处理order信息
+// pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+//     let mut order_info = Vec::new();
+//     for item in res_data_json.iter() {
+//         order_info.push(format_order_item(item.clone(), ct_val));
+//     }
+//     trace!(?order_info);
+//     SpecialOrder {
+//         name: res_data.label,
+//         order: order_info,
+//     }
+// }
+//
+// // 处理订单信息
+// pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+//     let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+//     let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
+//     let status = order["status"].as_str().unwrap_or("");
+//     let filled_size = Decimal::from_str(order["filledSize"].as_str().unwrap()).unwrap();
+//
+//     let avg_price = price;
+//
+//     let amount = size * ct_val;
+//     let deal_amount = filled_size * ct_val;
+//     let custom_status = if ["done"].contains(&status) {
+//         "REMOVE".to_string()
+//     } else if ["open", "match", "update"].contains(&status) {
+//         "NEW".to_string()
+//     } else {
+//         "NULL".to_string()
+//     };
+//     Order {
+//         id: order["orderId"].as_str().unwrap().to_string(),
+//         custom_id: order["clientOid"].as_str().unwrap().to_string(),
+//         price,
+//         amount,
+//         deal_amount,
+//         avg_price,
+//         status: custom_status,
+//         order_type: order["orderType"].as_str().unwrap().to_string(),
+//         trace_stack: TraceStack::new(0, Instant::now()).on_special("89 bitget_spot_handle".to_string()),
+//     }
+// }
+//
+// // 处理特殊深度数据
+// pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+//     HandleSwapInfo::handle_special_depth(ExchangeEnum::KucoinSpot, res_data)
+// }
+//
+// // 格式化深度信息
+// pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+//     let mut depth_items: Vec<MarketOrder> = vec![];
+//     for value in value.as_array().unwrap() {
+//         depth_items.push(MarketOrder {
+//             price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+//             amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+//         })
+//     }
+//     return depth_items;
+// }
+//
+// // 处理特殊Ticker信息
+// pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+//     let res_data_str = res_data.data;
+//     let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
+//     format_special_ticker(res_data_json, res_data.label)
+// }
+//
+// pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+//     let bp = Decimal::from_str(data["bestBid"].as_str().unwrap()).unwrap();
+//     let bq = Decimal::from_str(data["bestBidSize"].as_str().unwrap()).unwrap();
+//     let ap = Decimal::from_str(data["bestAsk"].as_str().unwrap()).unwrap();
+//     let aq = Decimal::from_str(data["bestAskSize"].as_str().unwrap()).unwrap();
+//     let mp = (bp + ap) * dec!(0.5);
+//     let t = Decimal::from_str(data["sequence"].as_str().unwrap()).unwrap();
+//     let create_at = data["time"].as_i64().unwrap() * 1000;
+//
+//     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+//     let depth_info = vec![bp, bq, ap, aq];
+//     SpecialDepth {
+//         name: label,
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at,
+//     }
+// }

+ 668 - 0
standard/src/kucoin_swap.rs

@@ -0,0 +1,668 @@
+use std::collections::{BTreeMap};
+use std::io::{Error, ErrorKind};
+use std::str::FromStr;
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use rust_decimal_macros::dec;
+use serde_json::{json, Value};
+use tokio::time::Instant;
+use tracing::{error, info};
+use exchanges::kucoin_swap_rest::KucoinSwapRest;
+use global::trace_stack::TraceStack;
+use crate::exchange::ExchangeEnum;
+use crate::{Account, kucoin_handle, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct KucoinSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: KucoinSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl KucoinSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> KucoinSwap {
+        let market = Market::new();
+        let mut kucoin_swap = KucoinSwap {
+            exchange: ExchangeEnum::KucoinSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: KucoinSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        kucoin_swap.market = KucoinSwap::get_market(&mut kucoin_swap).await.unwrap_or(kucoin_swap.market);
+
+        // 开启自动追加保证金
+        let append_rst = kucoin_swap.set_auto_deposit_status(true).await;
+
+        info!("设置自动追加保证金:{:?}", append_rst.unwrap());
+
+        return kucoin_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for KucoinSwap {
+    // 克隆方法
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+    fn get_self_exchange(&self) -> ExchangeEnum {
+        ExchangeEnum::KucoinSwap
+    }
+    // 获取交易对
+    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 {
+            Ok(res_data.data.to_string())
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 获取账号信息
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_array: Vec<&str> = symbol_mapper.split("_").collect();
+        let res_data = self.request.get_account(symbol_array[1].to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+
+            let balance = Decimal::from_f64(res_data_json["marginBalance"].as_f64().unwrap()).unwrap();
+            let available_balance = Decimal::from_f64(res_data_json["availableBalance"].as_f64().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, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_position(symbol_format).await;
+        if res_data.code == 200 {
+            let result = kucoin_handle::format_position_item(&res_data.data, &ct_val);
+            Ok(vec![result])
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_array: Vec<&str> = symbol_mapper.split("_").collect();
+        let res_data = self.request.get_positions(symbol_array[1].to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let mut result = Vec::new();
+            for item in res_data_json.iter() {
+                result.push(kucoin_handle::format_position_item(item, &Decimal::ONE))
+            }
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let res_data = self.request.get_ticker(symbol_format).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let ticker_info = res_data_json;
+            let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
+
+            let result = Ticker {
+                time,
+                high: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
+                last: Decimal::from_str(ticker_info["price"].as_str().unwrap()).unwrap(),
+                volume: Decimal::from_str(&ticker_info["size"].to_string()).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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper, ""));
+        let res_data = self.request.get_ticker(symbol_format).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let ticker_info = res_data_json;
+            let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
+            let result = Ticker {
+                time,
+                high: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
+                last: Decimal::from_str(ticker_info["price"].as_str().unwrap()).unwrap(),
+                volume: Decimal::from_str(&ticker_info["size"].to_string()).unwrap(),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let res_data = self.request.get_market_details().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["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("kucoin_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
+                    let base_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, base_asset.as_str());
+                    let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
+                    let quote_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, quote_asset.as_str());
+                    let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
+                    let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
+                    let ct_val = Decimal::from_f64(value["multiplier"].as_f64().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(ct_val.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset_mapper, quote_asset_mapper),
+                        base_asset: base_asset_mapper,
+                        quote_asset: quote_asset_mapper,
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
+                        min_notional,
+                        max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
+                        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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let res_data = self.request.get_market_details().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["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("kucoin_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
+                    let base_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, base_asset.as_str());
+                    let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
+                    let quote_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, quote_asset.as_str());
+                    let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
+                    let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
+                    let ct_val = Decimal::from_f64(value["multiplier"].as_f64().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(ct_val.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset_mapper, quote_asset_mapper),
+                        base_asset: base_asset_mapper,
+                        quote_asset: quote_asset_mapper,
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
+                        min_notional,
+                        max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
+                        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 ct_val = self.market.ct_val;
+        let res_data = self.request.get_orders_details(order_id.to_string(), custom_id.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json = 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_orders(status.to_string(), symbol_format.clone()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let order_list = res_data_json["items"].as_array().unwrap().clone();
+            let order_info: Vec<_> = order_list.iter().filter(|item| item["symbol"].as_str().unwrap_or("") == symbol_format.clone()).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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let ct_val = self.market.ct_val;
+        let mut params = json!({
+            "clientOid": custom_id,
+            "symbol": symbol_format,
+            "leverage": "10",
+            "reduceOnly":false,
+            "price": price.to_string(),
+        });
+        params["type"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
+        let size = (amount / ct_val).floor();
+        params["size"] = json!(size);
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+            }
+            "pd" => {
+                params["side"] = json!("sell");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+            }
+            "pk" => {
+                params["side"] = json!("buy");
+            }
+            _ => { error!("下单参数错误"); }
+        };
+
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let id = res_data_json["orderId"].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id: custom_id.to_string(),
+                price,
+                amount,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("359 kucoin_swap".to_string()),
+            };
+            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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let mut params = json!({
+            "clientOid": custom_id,
+            "symbol": symbol_format,
+            "leverage": "10",
+            "reduceOnly":false,
+            "price": price.to_string(),
+        });
+        let size = (amount / ct_val).floor();
+        params["size"] = json!(size);
+        if price.eq(&Decimal::ZERO){
+            params["type"] = json!("market");
+        }
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+            }
+            "pd" => {
+                params["side"] = json!("sell");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+            }
+            "pk" => {
+                params["side"] = json!("buy");
+            }
+            _ => { error!("下单参数错误"); }
+        };
+
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let id = res_data_json["orderId"].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id: custom_id.to_string(),
+                price,
+                amount,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("408 kucoin_swap".to_string()),
+            };
+            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 res_data = self.request.cancel_order(order_id.to_string(), custom_id.to_string()).await;
+        if order_id == "" {
+            error!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id);
+            panic!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id)
+        }
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
+            let id = cancel_ids[0].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id: custom_id.to_string(),
+                price: Decimal::ZERO,
+                amount: Decimal::ZERO,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "REMOVE".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("436 kucoin_swap".to_string()),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let res_data = self.request.cancel_orders(symbol_format).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
+            let result = cancel_ids.iter().map(|item|
+                Order {
+                    id: item.as_str().unwrap().to_string(),
+                    custom_id: "".to_string(),
+                    price: Decimal::ZERO,
+                    amount: Decimal::ZERO,
+                    deal_amount: Decimal::ZERO,
+                    avg_price: Decimal::ZERO,
+                    status: "REMOVE".to_string(),
+                    order_type: "".to_string(),
+                    trace_stack: TraceStack::new(0, Instant::now()).on_special("461 kucoin_swap".to_string()),
+                }
+            ).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 res_data = self.request.cancel_order_all().await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data;
+            let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
+            let result = cancel_ids.iter().map(|item|
+                Order {
+                    id: item.as_str().unwrap().to_string(),
+                    custom_id: "".to_string(),
+                    price: Decimal::ZERO,
+                    amount: Decimal::ZERO,
+                    deal_amount: Decimal::ZERO,
+                    avg_price: Decimal::ZERO,
+                    status: "REMOVE".to_string(),
+                    order_type: "".to_string(),
+                    trace_stack: TraceStack::new(0, Instant::now()).on_special("486 kucoin_swap".to_string()),
+                }
+            ).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_auto_deposit_status(&mut self, status: bool) -> Result<String, Error> {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let res_data = self.request.auto_deposit_status(symbol_format, status).await;
+        if res_data.code == 200 {
+            Ok(res_data.data.to_string())
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 指令下单
+    async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
+        let mut handles = vec![];
+        // 下单指令,limits_open里已经包含了limits_close,在core里面处理过了
+        for item in order_command.limits_open.keys() {
+            let mut ts = trace_stack.clone();
+
+            let amount = Decimal::from_str(&*order_command.limits_open[item].get(0).unwrap().clone()).unwrap();
+            let side = order_command.limits_open[item].get(1).unwrap().clone();
+            let price = Decimal::from_str(&*order_command.limits_open[item].get(2).unwrap().clone()).unwrap();
+            let cid = order_command.limits_open[item].get(3).unwrap().clone();
+
+            let mut self_clone = self.clone();
+            let handle = tokio::spawn(async move {
+                ts.on_before_send();
+                let result = self_clone.take_order(&cid, &side, price, amount).await;
+                ts.on_after_send();
+
+                match result {
+                    Ok(mut result) => {
+                        // ts.on_after_send();
+                        result.trace_stack = ts.clone();
+
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        let mut err_order = Order::new();
+                        err_order.custom_id = cid.clone();
+                        err_order.status = "REMOVE".to_string();
+
+                        self_clone.order_sender.send(err_order).await.unwrap();
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(handles);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 撤销订单
+        let mut cancel_handlers = vec![];
+        for item in order_command.cancel.keys() {
+            let order_id = order_command.cancel[item].get(1).unwrap().clone();
+            let custom_id = order_command.cancel[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+
+            let handle = tokio::spawn(async move {
+                if order_id != "" {
+                    let result = self_clone.cancel_order(&order_id, &custom_id).await;
+                    match result {
+                        Ok(_) => {
+                            // result_sd.send(result).await.unwrap();
+                        }
+                        Err(error) => {
+                            // 取消失败去查订单。
+                            let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+                            match query_rst {
+                                Ok(order) => {
+                                    self_clone.order_sender.send(order).await.unwrap();
+                                }
+                                Err(_query_err) => {
+                                    // error!(?_query_err);
+                                    // error!("撤单失败,而且查单也失败了,kucoin_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+                                }
+                            }
+                            self_clone.error_sender.send(error).await.unwrap();
+                        }
+                    }
+                }
+            });
+            cancel_handlers.push(handle)
+        }
+        let futures = FuturesUnordered::from_iter(cancel_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+
+        // 检查订单指令
+        let mut check_handlers = vec![];
+        for item in order_command.check.keys() {
+            let order_id = order_command.check[item].get(1).unwrap().clone();
+            let custom_id = order_command.check[item].get(0).unwrap().clone();
+
+            let mut self_clone = self.clone();
+
+            let handle = tokio::spawn(async move {
+                let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+                match result {
+                    Ok(result) => {
+                        self_clone.order_sender.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        self_clone.error_sender.send(error).await.unwrap();
+                    }
+                }
+            });
+            check_handlers.push(handle)
+        }
+
+        let futures = FuturesUnordered::from_iter(check_handlers);
+        // 等待所有任务完成
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+    }
+}
+
+pub fn format_order_item(order: Value, ct_val: Decimal) -> Order {
+    let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+    let size = Decimal::from_f64(order["size"].as_f64().unwrap()).unwrap();
+    let status = order["status"].as_str().unwrap_or("");
+    let filled_size = Decimal::from_f64(order["filledSize"].as_f64().unwrap()).unwrap();
+    let filled_value = Decimal::from_str(order["filledValue"].as_str().unwrap()).unwrap();
+
+    let amount = size * ct_val;
+    let deal_amount = filled_size * ct_val;
+    let avg_price = if deal_amount.is_zero() { Decimal::ZERO } else { filled_value / deal_amount };
+    let custom_status;
+    if ["cancelled", "closed", "finished"].contains(&status) {
+        custom_status = "REMOVE".to_string();
+    } else if status == "open" {
+        custom_status = "NEW".to_string();
+    } else {
+        custom_status = "NULL".to_string();
+    };
+    Order {
+        id: order["id"].as_str().unwrap().to_string(),
+        custom_id: order["clientOid"].as_str().unwrap_or("").to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["type"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("655 kucoin_swap".to_string()),
+    }
+}

+ 575 - 0
standard/src/lib.rs

@@ -0,0 +1,575 @@
+use std::collections::{BTreeMap, HashMap};
+use std::fmt;
+use std::fmt::Formatter;
+use std::io::{Error};
+use async_trait::async_trait;
+use rust_decimal::Decimal;
+use serde_json::Value;
+use tokio::time::Instant;
+use global::trace_stack::TraceStack;
+
+use crate::exchange::ExchangeEnum;
+
+// 引入工具模块
+pub mod utils;
+// 引入exchange模块
+pub mod exchange;
+pub mod handle_info;
+// 引入binance模块
+mod binance_swap;
+mod binance_spot;
+pub mod binance_swap_handle;
+pub mod binance_spot_handle;
+// 引入gate模块
+mod gate_swap;
+mod gate_spot;
+pub mod gate_swap_handle;
+mod kucoin_swap;
+pub mod kucoin_handle;
+mod okx_swap;
+pub mod okx_handle;
+mod bitget_spot;
+pub mod bitget_spot_handle;
+mod kucoin_spot;
+pub mod kucoin_spot_handle;
+mod bybit_swap;
+mod bybit_swap_handle;
+mod bitget_swap;
+mod bitget_swap_handle;
+
+/// 持仓模式枚举
+/// - `Both`:单持仓方向
+/// - `LONG`:多仓
+/// - `SHORT`:空仓
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PositionModeEnum {
+    Both,
+    Long,
+    Short,
+}
+
+/// OrderCommand结构体(下单指令)
+/// - `cancel(HashMap<String, Vec<String>)`: 取消订单指令 `{"order_name": [c_id, o_id]}`;
+/// - `check(HashMap<String, Vec<String>>)`: balance挂单的冻结数量 `{"order_name": [数量,方向,价格,c_id]}`;
+/// - `limits_open(HashMap<String, Vec<String>>)`: 总计交易币数量 `{"order_name": [c_id, o_id]}`;
+/// - `limits_close(HashMap<String, Vec<String>>)`: 可用交易币数量 `{"order_name": [c_id, o_id]}`;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct OrderCommand {
+    // 取消订单指令,数据结构例子:
+    pub cancel: HashMap<String, Vec<String>>,
+    // 检验指令,数据结构例子:(暂没找到例子)
+    pub check: HashMap<String, Vec<String>>,
+    // 限开指令,数据结构例子:(暂没找到例子)
+    pub limits_open: HashMap<String, Vec<String>>,
+    // 限收指令,数据结构例子:(暂没找到例子)
+    pub limits_close: HashMap<String, Vec<String>>,
+}
+
+impl OrderCommand {
+    pub fn new() -> OrderCommand {
+        OrderCommand {
+            cancel: Default::default(),
+            check: Default::default(),
+            limits_open: Default::default(),
+            limits_close: Default::default(),
+        }
+    }
+    pub fn is_not_empty(&self) -> bool {
+        let is_empty = self.limits_close.is_empty() && self.limits_open.is_empty() && self.cancel.is_empty() && self.check.is_empty();
+        if is_empty { false } else { true }
+    }
+}
+
+impl fmt::Display for OrderCommand {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "订单触发数据: cancel: {:?}, check: {:?}, limits_open: {:?}, limits_close: {:?}",
+               self.cancel,
+               self.check,
+               self.limits_open,
+               self.limits_close
+        )
+    }
+}
+
+/// 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(),
+        }
+    }
+}
+
+/// Depth结构体(市场深度)
+/// - `time(i64)`: 深度更新时间戳(ms);
+/// - `asks(Vec<MarketOrder>)`: 卖方深度列表;
+/// - `bids(Vec<MarketOrder>)`: 买方深度列表;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Depth {
+    pub time: i64,
+    pub asks: Vec<MarketOrder>,
+    pub bids: Vec<MarketOrder>,
+}
+
+impl Depth {
+    pub fn new() -> Depth {
+        Depth {
+            time: 0,
+            asks: vec![],
+            bids: vec![],
+        }
+    }
+}
+
+/// 特殊Depth结构体(市场深度)
+/// - `name<String>`: 平台信息;
+/// - `depth(Vec<Decimal>)`: 深度信息;
+/// - `ticker(SpecialTicker)`: 市场行情;
+/// - `t(Decimal)`: 数据更新id
+/// - `create_at(i64)`: 数据生成时间
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SpecialDepth {
+    pub name: String,
+    pub depth: Vec<Decimal>,
+    pub ticker: SpecialTicker,
+    pub t: Decimal,
+    pub create_at: i64,
+}
+
+impl SpecialDepth {
+    pub fn new() -> SpecialDepth {
+        SpecialDepth {
+            name: "".to_string(),
+            depth: vec![],
+            ticker: SpecialTicker::new(),
+            t: Default::default(),
+            create_at: 0,
+        }
+    }
+}
+
+/// 特殊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,
+        }
+    }
+}
+
+/// MarketOrder结构体(市场深度单)
+/// - `price(Decimal)`: 价格
+/// - `amount(Decimal)`: 数量
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct MarketOrder {
+    pub price: Decimal,
+    pub amount: Decimal,
+}
+
+impl MarketOrder {
+    pub fn new() -> MarketOrder {
+        MarketOrder {
+            price: Default::default(),
+            amount: Default::default(),
+        }
+    }
+}
+
+/// Record结构体(标准的OHLC结构)
+/// - `time(i64)`: 时间戳;
+/// - `open(Decimal)`: 开盘价;
+/// - `high(Decimal)`: 最高价;
+/// - `low(Decimal):` 最低价;
+/// - `close(Decimal)`: 收盘价;
+/// - `volume(Decimal)`: 交易量;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Record {
+    pub time: i64,
+    pub open: Decimal,
+    pub high: Decimal,
+    pub low: Decimal,
+    pub close: Decimal,
+    pub volume: Decimal,
+}
+
+impl Record {
+    pub fn new() -> Record {
+        Record {
+            time: 0,
+            open: Default::default(),
+            high: Default::default(),
+            low: Default::default(),
+            close: Default::default(),
+            volume: Default::default(),
+        }
+    }
+}
+
+/// 特殊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,
+    pub trace_stack: TraceStack,
+}
+
+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(),
+            trace_stack: TraceStack::new(0, Instant::now()),
+        }
+    }
+}
+
+/// 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>;
+    // 指令下单
+    async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack);
+}

+ 198 - 0
standard/src/okx_handle.rs

@@ -0,0 +1,198 @@
+// use std::str::FromStr;
+// use rust_decimal::Decimal;
+// use rust_decimal_macros::dec;
+// use serde::{Deserialize, Serialize};
+// use tokio::time::Instant;
+// use tracing::trace;
+// use exchanges::response_base::ResponseData;
+// use global::trace_stack::TraceStack;
+// use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
+// use crate::exchange::ExchangeEnum;
+// use crate::handle_info::HandleSwapInfo;
+// use crate::okx_swap::SwapPosition;
+//
+//
+// #[derive(Debug, Clone, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// pub struct SwapBalanceAndPositionSubscribe {
+//     pos_data: Vec<SwapBalanceAndPositionPosDataSubscribe>,
+// }
+//
+// #[derive(Debug, Clone, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// pub struct SwapBalanceAndPositionPosDataSubscribe {
+//     pos_id: String,
+//     trade_id: String,
+//     inst_id: String,
+//     inst_type: String,
+//     mgn_mode: String,
+//     pos_side: String,
+//     pos: Decimal,
+//     ccy: String,
+//     pos_ccy: String,
+//     avg_px: Decimal,
+//     u_time: String,
+// }
+//
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SwapPositionSubscribe {
+//     arg: SwapPositionSubscribeArg,
+//     data: Vec<SwapPosition>,
+// }
+//
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SwapPositionSubscribeArg {
+//     channel: String,
+//     uid: String,
+//     inst_type: String,
+// }
+//
+// // 处理账号信息
+// pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
+//     let res_data_str = res_data.data;
+//     let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+//     let account_info = res_data_json[0]["details"].clone();
+//     let details = account_info[0].clone();
+//     format_account_info(details, symbol)
+// }
+//
+// pub fn format_account_info(data: serde_json::Value, symbol: String) -> Account {
+//     let symbol_upper = symbol.to_uppercase();
+//     let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+//     Account {
+//         coin: symbol_array[1].to_string(),
+//         balance: Decimal::from_str(data["cashBal"].as_str().unwrap()).unwrap(),
+//         available_balance: Decimal::from_str(data["availBal"].as_str().unwrap()).unwrap(),
+//         frozen_balance: Decimal::from_str(data["fixedBal"].as_str().unwrap()).unwrap(),
+//         stocks: Decimal::ZERO,
+//         available_stocks: Decimal::ZERO,
+//         frozen_stocks: Decimal::ZERO,
+//     }
+// }
+//
+// // 处理order信息
+// pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+//     let res_data_str = res_data.data;
+//     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+//     trace!(?res_data_json);
+//     let mut order_info = Vec::new();
+//     for item in res_data_json.iter() {
+//         order_info.push(format_order_item(item.clone(), ct_val));
+//     }
+//     trace!(?order_info);
+//     SpecialOrder {
+//         name: res_data.label,
+//         order: order_info,
+//     }
+// }
+//
+// // 处理订单信息
+// pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+//     let price = Decimal::from_str(order["px"].as_str().unwrap()).unwrap();
+//     let size = Decimal::from_str(order["sz"].as_str().unwrap()).unwrap();
+//     let status = order["state"].as_str().unwrap_or("");
+//     let filled_size = Decimal::from_str(order["accFillSz"].as_str().unwrap()).unwrap();
+//
+//     let avg_price = Decimal::from_str(order["avgPx"].as_str().unwrap()).unwrap();
+//
+//     let amount = size * ct_val;
+//     let deal_amount = filled_size * ct_val;
+//     let custom_status = if ["canceled", "filled", "mmp_canceled"].contains(&status) {
+//         "REMOVE".to_string()
+//     } else if ["live", "partially_filled"].contains(&status) {
+//         "NEW".to_string()
+//     } else {
+//         "NULL".to_string()
+//     };
+//     Order {
+//         id: order["ordId"].as_str().unwrap().to_string(),
+//         custom_id: order["clOrdId"].as_str().unwrap().to_string(),
+//         price,
+//         amount,
+//         deal_amount,
+//         avg_price,
+//         status: custom_status,
+//         order_type: order["ordType"].as_str().unwrap().to_string(),
+//         trace_stack: TraceStack::new(0, Instant::now()).on_special("77 okx_handle".to_string()),
+//     }
+// }
+//
+// // 处理特殊深度数据
+// pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+//     HandleSwapInfo::handle_special_depth(ExchangeEnum::OkxSwap, res_data)
+// }
+//
+// // 格式化深度信息
+// pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+//     let mut depth_items: Vec<MarketOrder> = vec![];
+//     for value in value.as_array().unwrap() {
+//         depth_items.push(MarketOrder {
+//             price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+//             amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+//         })
+//     }
+//     return depth_items;
+// }
+//
+// // 处理特殊Ticker信息
+// pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+//     let res_data_str = res_data.data;
+//     let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
+//     format_special_ticker(res_data_json[0].clone(), res_data.label)
+// }
+//
+// pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+//     let bids = data["bids"][0].as_array().unwrap();
+//     let asks = data["asks"][0].as_array().unwrap();
+//     let bp = Decimal::from_str(bids[0].as_str().unwrap()).unwrap();
+//     let bq = Decimal::from_str(bids[1].as_str().unwrap()).unwrap();
+//     let ap = Decimal::from_str(asks[0].as_str().unwrap()).unwrap();
+//     let aq = Decimal::from_str(asks[1].as_str().unwrap()).unwrap();
+//     let mp = (bp + ap) * dec!(0.5);
+//     let t = Decimal::from_str(&data["seqId"].to_string()).unwrap();
+//     let create_at = data["ts"].as_str().unwrap().parse::<i64>().unwrap() * 1000;
+//
+//     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+//     let depth_info = vec![bp, bq, ap, aq];
+//     SpecialDepth {
+//         name: label,
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at,
+//     }
+// }
+//
+//
+// // 处理position信息
+// pub fn handle_position(res_data: ResponseData, ct_val: Decimal) -> Vec<Position> {
+//     let res_data_str = res_data.data;
+//     let data_list: Vec<SwapBalanceAndPositionSubscribe> = serde_json::from_str(&res_data_str).unwrap();
+//
+//     let position_data = data_list[0].pos_data.clone();
+//     if position_data.len() > 0 {
+//         position_data.iter().map(|item| format_position_item(item, ct_val)).collect()
+//     } else {
+//         vec![]
+//     }
+// }
+//
+// pub fn format_position_item(value: &SwapBalanceAndPositionPosDataSubscribe, ct_val: Decimal) -> Position {
+//     let position_mode = match value.pos_side.as_str() {
+//         "long" => { PositionModeEnum::Long }
+//         "short" => { PositionModeEnum::Short }
+//         _ => { PositionModeEnum::Both }
+//     };
+//     Position {
+//         symbol: value.inst_id.replace("-SWAP", ""),
+//         margin_level: Decimal::ZERO,
+//         amount: value.pos * ct_val,
+//         frozen_amount: Decimal::ZERO,
+//         price: value.avg_px,
+//         profit: Decimal::ZERO,
+//         position_mode,
+//         margin: Decimal::ZERO,
+//     }
+// }

+ 1072 - 0
standard/src/okx_swap.rs

@@ -0,0 +1,1072 @@
+// use std::collections::{BTreeMap, HashMap};
+// use std::io::{Error, ErrorKind};
+// use std::str::FromStr;
+// use tokio::sync::mpsc::Sender;
+// use async_trait::async_trait;
+// use futures::stream::FuturesUnordered;
+// use futures::TryStreamExt;
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::FromPrimitive;
+// use serde::{Deserialize, Serialize};
+// use serde_json::json;
+// use tokio::time::Instant;
+// use tracing::{debug, error};
+// use exchanges::okx_swap_rest::OkxSwapRest;
+// use global::trace_stack::TraceStack;
+// use crate::exchange::ExchangeEnum;
+// use crate::{Account, Market, Order, OrderCommand, Platform, Position, PositionModeEnum, Ticker, utils};
+//
+// /// Okx交易所账户信息请求数据结构
+// /// - 接口`"/api/v5/account/balance"`
+// ///
+// /// struct SwapAccount
+// /// - `adj_eq`: String, 美金层面有效保证金
+// /// - `borrow_froz`: String, 账户美金层面潜在借币占用保证金
+// /// - `details`: Vec<SwapAccountDetails>, 各币种资产详细信息
+// /// - `imr`: String, 美金层面占用保证金
+// /// - `iso_eq`: String, 美金层面逐仓仓位权益
+// /// - `mgn_ratio`: String, 美金层面保证金率
+// /// - `mmr`: String, 美金层面维持保证金
+// /// - `notional_usd`: String, 以美金价值为单位的持仓数量,即仓位美金价值
+// /// - `ord_froz`: String, 美金层面全仓挂单占用保证金
+// /// - `total_eq`: String, 美金层面权益
+// /// - `u_time`: String, 账户信息的更新时间
+// ///
+// /// struct SwapAccountDetails
+// /// - `avail_bal`: Decimal, 可用余额
+// /// - `avail_eq`: Decimal, 可用保证金
+// /// - `cash_bal`: Decimal, 币种余额
+// /// - `ccy`: String, 币种
+// /// - `cross_liab`: String, 币种全仓负债额
+// /// - `dis_eq`: Decimal, 美金层面币种折算权益
+// /// - `eq`: Decimal, 币种总权益
+// /// - `eq_usd`: Decimal, 币种权益美金价值
+// /// - `fixed_bal`: Decimal, 币种冻结金额
+// /// - `frozen_bal`: Decimal, 币种占用金额
+// /// - `interest`:String, 计息,应扣未扣利息。
+// /// - `iso_eq`: Decimal, 币种逐仓仓位权益
+// /// - `iso_liab`: String, 逐仓未实现盈亏
+// /// - `iso_upl`: Decimal, 币种逐仓负债额
+// /// - `liab`: String, 币种负债额
+// /// - `max_loan`: String, 币种最大可借
+// /// - `mgn_ratio`: String, 保证金率
+// /// - `notional_lever`: Decimal, 币种杠杆倍数
+// /// - `ord_frozen`: Decimal, 挂单冻结数量
+// /// - `twap`: Decimal, 当前负债币种触发系统自动换币的风险
+// /// - `u_time`: String, 币种余额信息的更新时间
+// /// - `upl`: Decimal, 未实现盈亏
+// /// - `upl_liab`: String, 由于仓位未实现亏损导致的负债
+// /// - `stgy_eq`: Decimal, 策略权益
+// /// - `spot_in_use_amt`: String, 现货对冲占用数量
+// /// - `borrow_froz`: String, 币种美金层面潜在借币占用保证金
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SwapAccount {
+//     adj_eq: String,
+//     borrow_froz: String,
+//     details: Vec<SwapAccountDetails>,
+//     imr: String,
+//     iso_eq: String,
+//     mgn_ratio: String,
+//     mmr: String,
+//     notional_usd: String,
+//     ord_froz: String,
+//     total_eq: String,
+//     u_time: String,
+// }
+//
+// #[derive(Debug, Clone, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SwapAccountDetails {
+//     avail_bal: Decimal,
+//     avail_eq: Decimal,
+//     cash_bal: Decimal,
+//     ccy: String,
+//     cross_liab: String,
+//     dis_eq: Decimal,
+//     eq: Decimal,
+//     eq_usd: Decimal,
+//     fixed_bal: Decimal,
+//     frozen_bal: Decimal,
+//     interest: String,
+//     iso_eq: Decimal,
+//     iso_liab: String,
+//     iso_upl: Decimal,
+//     liab: String,
+//     max_loan: String,
+//     mgn_ratio: String,
+//     notional_lever: Decimal,
+//     ord_frozen: Decimal,
+//     twap: Decimal,
+//     u_time: String,
+//     upl: Decimal,
+//     upl_liab: String,
+//     stgy_eq: Decimal,
+//     spot_in_use_amt: String,
+//     borrow_froz: String,
+// }
+//
+// /// Okx交易所持仓信息请求数据结构
+// /// - 接口`"/api/v5/account/positions"`
+// ///
+// /// struct SwapPosition
+// /// - `adl`: Decimal, 信号区
+// /// - `avail_pos`: Decimal, 可平仓数量,适用于 币币杠杆,交割/永续(开平仓模式)
+// /// - `avg_px`: Decimal, 开仓平均价
+// /// - `c_time`: String, 持仓创建时间
+// /// - `ccy`: String, 占用保证金的币种
+// /// - `delta_b_s`: String, 美金本位持仓仓位delta,仅适用于期权
+// /// - `delta_p_a`: String, 币本位持仓仓位delta,仅适用于期权
+// /// - `gamma_b_s`: String, 美金本位持仓仓位gamma,仅适用于期权
+// /// - `gamma_p_a`: String, 币本位持仓仓位gamma,仅适用于期权
+// /// - `imr`: String, 初始保证金,仅适用于全仓
+// /// - `inst_id`: String, 产品ID
+// /// - `inst_type`: String, 产品类型
+// /// - `interest`: Decimal, 利息,已经生成的未扣利息
+// /// - `idx_px`: Decimal, 最新指数价格
+// /// - `last`: Decimal, 最新成交价
+// /// - `usd_px`: String, 美金价格
+// /// - `be_px`: Decimal, 盈亏平衡价
+// /// - `lever`: Decimal, 杠杆倍数,不适用于期权
+// /// - `liab`: String, 负债额,仅适用于币币杠杆
+// /// - `liab_ccy`: String, 负债币种,仅适用于币币杠杆
+// /// - `liq_px`: Decimal, 预估强平价
+// /// - `mark_px`: Decimal, 最新标记价格
+// /// - `margin`: Decimal, 保证金余额,可增减,仅适用于逐仓
+// /// - `mgn_mode`: Decimal, 保证金模式
+// /// - `mgn_ratio`: Decimal, 保证金率
+// /// - `mmr`: Decimal, 维持保证金
+// /// - `notional_usd`: Decimal, 以美金价值为单位的持仓数量
+// /// - `opt_val`: String, 期权市值,仅适用于期权
+// /// - `p_time`: String,
+// /// - `pos`: Decimal, 持仓数量,逐仓自主划转模式下,转入保证金后会产生pos为0的仓位
+// /// - `pos_ccy`: String, 仓位资产币种,仅适用于币币杠杆仓位
+// /// - `pos_id`: Decimal, 持仓ID
+// /// - `pos_side`: String, 持仓方向
+// /// - `spot_in_use_amt`: String,
+// /// - `spot_in_use_ccy`: String,
+// /// - `theta_b_s`: String, 美金本位持仓仓位theta,仅适用于期权
+// /// - `theta_p_a`: String, 币本位持仓仓位theta,仅适用于期权
+// /// - `trade_id`: Decimal, 最新成交ID
+// /// - `biz_ref_id`: String, 外部业务id
+// /// - `biz_ref_type`: String, 外部业务类型
+// /// - `quote_bal`: Decimal, 计价币余额 ,适用于 币币杠杆(逐仓自主划转模式 和 一键借币模式)
+// /// - `base_bal`: Decimal, 交易币余额,适用于 币币杠杆(逐仓自主划转模式 和 一键借币模式)
+// /// - `base_borrowed`: String, 交易币已借,适用于 币币杠杆(逐仓一键借币模式)
+// /// - `base_interest`: String, 交易币计息,适用于 币币杠杆(逐仓一键借币模式)
+// /// - `quote_borrowed`: String, 计价币已借,适用于 币币杠杆(逐仓一键借币模式)
+// /// - `quote_interest`: String, 计价币计息,适用于 币币杠杆(逐仓一键借币模式)
+// /// - `u_time`: String, 最近一次持仓更新时间
+// /// - `upl`: Decimal, 未实现收益(以标记价格计算)
+// /// - `upl_last_px`: Decimal, 以最新成交价格计算的未实现收益,主要做展示使用,实际值还是 upl
+// /// - `upl_ratio`: Decimal, 未实现收益率(以标记价格计算
+// /// - `upl_ratio_last_px`: Decimal, 以最新成交价格计算的未实现收益率
+// /// - `vega_b_s`: String, 美金本位持仓仓位vega,仅适用于期权
+// /// - `vega_p_a`: String, 币本位持仓仓位vega,仅适用于期权
+// /// - `realized_pnl`: Decimal, 已实现收益
+// /// - `pnl`: Decimal, 平仓订单累计收益额
+// /// - `fee`: Decimal, 累计手续费金额,正数代表平台返佣 ,负数代表平台扣除
+// /// - `funding_fee`: Decimal, 累计资金费用
+// /// - `liq_penalty`: Decimal, 累计爆仓罚金,有值时为负数。
+// /// - `close_order_algo`: Vec<SwapPositionCloseOrderAlgo>, 	平仓策略委托订单
+// ///
+// /// struct SwapPositionCloseOrderAlgo
+// ///
+// /// - `algo_id`: String, 策略委托单ID
+// /// - `sl_trigger_px`: Decimal, 止损触发价
+// /// - `sl_trigger_px_type`: String, 止损触发价类型
+// /// - `tp_trigger_px`: Decimal, 止盈委托价
+// /// - `tp_trigger_px_type`: String, 止盈触发价类型
+// /// - `close_fraction`: Decimal, 策略委托触发时
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// pub struct SwapPosition {
+//     pub adl: String,
+//     pub avail_pos: String,
+//     pub avg_px: Decimal,
+//     pub c_time: String,
+//     pub ccy: String,
+//     pub delta_b_s: String,
+//     pub delta_p_a: String,
+//     pub gamma_b_s: String,
+//     pub gamma_p_a: String,
+//     pub imr: String,
+//     pub inst_id: String,
+//     pub inst_type: String,
+//     pub interest: String,
+//     pub idx_px: String,
+//     pub last: String,
+//     pub usd_px: String,
+//     pub be_px: String,
+//     pub lever: Decimal,
+//     pub liab: String,
+//     pub liab_ccy: String,
+//     pub liq_px: String,
+//     pub mark_px: String,
+//     pub margin: String,
+//     pub mgn_mode: String,
+//     pub mgn_ratio: String,
+//     pub mmr: String,
+//     pub notional_usd: String,
+//     pub opt_val: String,
+//     pub pos: Decimal,
+//     pub pos_ccy: String,
+//     pub pos_id: String,
+//     pub pos_side: String,
+//     pub spot_in_use_amt: String,
+//     pub spot_in_use_ccy: String,
+//     pub theta_b_s: String,
+//     pub theta_p_a: String,
+//     pub trade_id: String,
+//     pub biz_ref_id: String,
+//     pub biz_ref_type: String,
+//     pub quote_bal: String,
+//     pub base_bal: String,
+//     pub base_borrowed: String,
+//     pub base_interest: String,
+//     pub quote_borrowed: String,
+//     pub quote_interest: String,
+//     pub u_time: String,
+//     pub upl: Decimal,
+//     pub upl_last_px: String,
+//     pub upl_ratio: String,
+//     pub upl_ratio_last_px: String,
+//     pub vega_b_s: String,
+//     pub vega_p_a: String,
+//     pub realized_pnl: String,
+//     pub pnl: String,
+//     pub fee: String,
+//     pub funding_fee: String,
+//     pub liq_penalty: String,
+//     pub close_order_algo: Vec<SwapPositionCloseOrderAlgo>,
+// }
+//
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// pub struct SwapPositionCloseOrderAlgo {
+//     pub algo_id: String,
+//     pub sl_trigger_px: String,
+//     pub sl_trigger_px_type: String,
+//     pub tp_trigger_px: String,
+//     pub tp_trigger_px_type: String,
+//     pub close_fraction: String,
+// }
+//
+// /// Okx交易所行情信息请求数据结构
+// /// - 接口`"/api/v5/market/ticker"`
+// ///
+// /// struct SwapTicker
+// /// - `inst_type`: String, 产品类型
+// /// - `inst_id`: String, 产品ID
+// /// - `last`: Decimal, 最新成交价
+// /// - `last_sz`: Decimal, 最新成交的数量
+// /// - `ask_px`: Decimal, 卖一价
+// /// - `ask_sz`: Decimal, 卖一价的挂单数数量
+// /// - `bid_px`: Decimal, 买一价
+// /// - `bid_sz`: Decimal, 买一价的挂单数量
+// /// - `open24h`: Decimal, 24小时开盘价
+// /// - `high24h`: Decimal, 24小时最高价
+// /// - `low24h`: Decimal, 24小时最低价
+// /// - `vol_ccy24h`: Decimal, 24小时成交量,以币为单位
+// /// - `vol24h`: Decimal, 24小时成交量,以张为单位
+// /// - `ts`: String, ticker数据产生时间
+// /// - `sod_utc0`: Decimal, UTC 0 时开盘价
+// /// - `sod_utc8`: Decimal, UTC+8 时开盘价
+// #[derive(Debug, Clone, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SwapTicker {
+//     inst_type: String,
+//     inst_id: String,
+//     last: Decimal,
+//     last_sz: Decimal,
+//     ask_px: Decimal,
+//     ask_sz: Decimal,
+//     bid_px: Decimal,
+//     bid_sz: Decimal,
+//     open24h: Decimal,
+//     high24h: Decimal,
+//     low24h: Decimal,
+//     vol_ccy24h: Decimal,
+//     vol24h: Decimal,
+//     ts: String,
+//     sod_utc0: Decimal,
+//     sod_utc8: Decimal,
+// }
+//
+// /// Okx交易所市场信息请求数据结构
+// /// - 接口`"/api/v5/public/instruments"`
+// ///
+// /// struct SwapMarket
+// /// - `alias`: String,
+// /// - `base_ccy`: String,
+// /// - `category`: String,
+// /// - `ct_mult`: Decimal,
+// /// - `ct_type`: String,
+// /// - `ct_val`: Decimal,
+// /// - `ct_val_ccy`: String,
+// /// - `exp_time`: String,
+// /// - `inst_family`: String,
+// /// - `inst_id`: String,
+// /// - `inst_type`: String,
+// /// - `lever`: Decimal,
+// /// - `list_time`: String,
+// /// - `lot_sz`: Decimal,
+// /// - `max_iceberg_sz`: Decimal,
+// /// - `max_lmt_sz`: Decimal,
+// /// - `max_mkt_sz`: Decimal,
+// /// - `max_stop_sz`: Decimal,
+// /// - `max_trigger_sz`: Decimal,
+// /// - `max_twap_sz`: Decimal,
+// /// - `min_sz`: Decimal,
+// /// - `opt_type`: String,
+// /// - `quote_ccy`: String,
+// /// - `settle_ccy`: String,
+// /// - `state`: String,
+// /// - `stk`: String,
+// /// - `tick_sz`: Decimal,
+// /// - `uly`: String,
+// #[derive(Debug, Clone, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct SwapMarket {
+//     alias: String,
+//     base_ccy: String,
+//     category: String,
+//     ct_mult: Decimal,
+//     ct_type: String,
+//     ct_val: Decimal,
+//     ct_val_ccy: String,
+//     exp_time: String,
+//     inst_family: String,
+//     inst_id: String,
+//     inst_type: String,
+//     lever: Decimal,
+//     list_time: String,
+//     lot_sz: Decimal,
+//     max_iceberg_sz: Decimal,
+//     max_lmt_sz: Decimal,
+//     max_mkt_sz: Decimal,
+//     max_stop_sz: Decimal,
+//     max_trigger_sz: Decimal,
+//     max_twap_sz: Decimal,
+//     min_sz: Decimal,
+//     opt_type: String,
+//     quote_ccy: String,
+//     settle_ccy: String,
+//     state: String,
+//     stk: String,
+//     tick_sz: Decimal,
+//     uly: String,
+// }
+//
+// /// Okx交易所订单信息请求数据结构
+// /// - 接口`"/api/v5/trade/order"`
+// ///
+// /// struct SwapOrder
+// /// - `inst_type`: String, 产品类型
+// /// - `inst_id`: String, 产品ID
+// /// - `ccy`: String, 保证金币种
+// /// - `ord_id`: String, 订单ID
+// /// - `cl_ord_id`: String, 客户自定义订单ID
+// /// - `tag`: String, 订单标签
+// /// - `px`: Decimal, 委托价格
+// /// - `px_usd`: String, 期权价格
+// /// - `px_vol`: String, 期权订单的隐含波动率
+// /// - `px_type`: String, 期权的价格类型
+// /// - `sz`: Decimal, 委托数量
+// /// - `pnl`: Decimal, 收益
+// /// - `ord_type`: String, 订单类型
+// /// - `side`: String, 订单方向
+// /// - `pos_side`: String, 持仓方向
+// /// - `td_mode`: String, 交易模式
+// /// - `acc_fill_sz`: Decimal, 累计成交数量
+// /// - `fill_px`: String, 最新成交价格,如果成交数量为0,该字段为""
+// /// - `trade_id`: String, 最新成交ID
+// /// - `fill_sz`: Decimal, 最新成交数量
+// /// - `fill_time`: String, 最新成交时间
+// /// - `source`: String, 订单来源
+// /// - `state`: String, 订单状态
+// /// - `avg_px`: Decimal, 成交均价,如果成交数量为0,该字段也为""
+// /// - `lever`: Decimal, 杠杆倍数
+// /// - `attach_algo_cl_ord_id`: String, 下单附带止盈止损时,客户自定义的策略订单ID
+// /// - `tp_trigger_px`: Decimal, 止盈触发价
+// /// - `tp_trigger_px_type`: String, 止盈触发价类型
+// /// - `tp_ord_px`: Decimal, 止盈委托价
+// /// - `sl_trigger_px`: Decimal, 止损触发价
+// /// - `sl_trigger_px_type`: String, 止损触发价类型
+// /// - `sl_ord_px`: Decimal, 止损委托价
+// /// - `stp_id`: String, 自成交保护ID
+// /// - `stp_mode`: String, 自成交保护模式
+// /// - `fee_ccy`: String, 交易手续费币种
+// /// - `fee`: Decimal, 手续费与返佣
+// /// - `rebate_ccy`: String, 返佣金币种
+// /// - `rebate`: String, 返佣金额,仅适用于币币和杠杆
+// /// - `tgt_ccy`: String, 币币市价单委托数量sz的单位
+// /// - `category`: String, 订单种类
+// /// - `reduce_only`: String, 是否只减仓
+// /// - `cancel_source`: String, 	订单取消来源的原因枚举值代码
+// /// - `cancel_source_reason`: String, 订单取消来源的对应具体原因
+// /// - `quick_mgn_type`: String, 一键借币类型,仅适用于杠杆逐仓的一键借币模式
+// /// - `algo_cl_ord_id`: String, 客户自定义策略订单ID
+// /// - `algo_id`: String, 策略委托单ID,策略订单触发时有值,否则为""
+// /// - `u_time`: String, 订单状态更新时间
+// /// - `c_time`: String, 订单创建时间
+// #[derive(Debug, Clone, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// pub struct SwapOrder {
+//     inst_type: String,
+//     inst_id: String,
+//     ccy: String,
+//     ord_id: String,
+//     cl_ord_id: String,
+//     tag: String,
+//     px: Decimal,
+//     px_usd: String,
+//     px_vol: String,
+//     px_type: String,
+//     sz: Decimal,
+//     pnl: String,
+//     ord_type: String,
+//     side: String,
+//     pos_side: String,
+//     td_mode: String,
+//     acc_fill_sz: Decimal,
+//     fill_px: String,
+//     trade_id: String,
+//     fill_sz: String,
+//     fill_time: String,
+//     source: String,
+//     state: String,
+//     avg_px: String,
+//     lever: String,
+//     attach_algo_cl_ord_id: String,
+//     tp_trigger_px: String,
+//     tp_trigger_px_type: String,
+//     tp_ord_px: String,
+//     sl_trigger_px: String,
+//     sl_trigger_px_type: String,
+//     sl_ord_px: String,
+//     stp_id: String,
+//     stp_mode: String,
+//     fee_ccy: String,
+//     fee: String,
+//     rebate_ccy: String,
+//     rebate: String,
+//     tgt_ccy: String,
+//     category: String,
+//     reduce_only: String,
+//     cancel_source: String,
+//     cancel_source_reason: String,
+//     quick_mgn_type: String,
+//     algo_cl_ord_id: String,
+//     algo_id: String,
+//     u_time: String,
+//     c_time: String,
+// }
+//
+// #[allow(dead_code)]
+// #[derive(Clone)]
+// pub struct OkxSwap {
+//     exchange: ExchangeEnum,
+//     symbol: String,
+//     is_colo: bool,
+//     params: BTreeMap<String, String>,
+//     request: OkxSwapRest,
+//     market: Market,
+//     order_sender: Sender<Order>,
+//     error_sender: Sender<Error>,
+// }
+//
+// impl OkxSwap {
+//     pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> OkxSwap {
+//         let market = Market::new();
+//         let mut okx_swap = OkxSwap {
+//             exchange: ExchangeEnum::OkxSwap,
+//             symbol: symbol.to_uppercase(),
+//             is_colo,
+//             params: params.clone(),
+//             request: OkxSwapRest::new(is_colo, params.clone()),
+//             market,
+//             order_sender,
+//             error_sender,
+//         };
+//         okx_swap.market = OkxSwap::get_market(&mut okx_swap).await.unwrap_or(okx_swap.market);
+//         return okx_swap;
+//     }
+// }
+//
+// #[async_trait]
+// impl Platform for OkxSwap {
+//     fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+//
+//     fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::OkxSwap }
+//
+//     fn get_self_symbol(&self) -> String { self.symbol.clone() }
+//
+//     fn get_self_is_colo(&self) -> bool { self.is_colo }
+//
+//     fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+//
+//     fn get_self_market(&self) -> Market { self.market.clone() }
+//
+//     fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+//
+//     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_str = &res_data.data;
+//             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+//             let result = res_data_json[0]["ts"].as_str().unwrap().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_balance(symbol_array[1].to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let balance_info_list: Vec<SwapAccount> = serde_json::from_str(res_data_str).unwrap();
+//             let detail = balance_info_list[0].details.iter().find(|&item| item.ccy == symbol_array[1]);
+//             match detail {
+//                 None => {
+//                     error!("Okx:获取Account信息错误!\nhandle_swap_account:res_data={:?}", res_data);
+//                     panic!("Okx:获取Account信息错误!\nhandle_swap_account:res_data={:?}", res_data)
+//                 }
+//                 Some(value) => {
+//                     let result = Account {
+//                         coin: value.ccy.to_uppercase(),
+//                         balance: value.avail_bal + value.fixed_bal,
+//                         available_balance: value.avail_bal,
+//                         frozen_balance: value.fixed_bal,
+//                         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, "okx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+//         let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.to_string(), "-"));
+//         let ct_val = self.market.ct_val;
+//         let res_data = self.request.get_positions("SWAP".to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let data_list: Vec<SwapPosition> = serde_json::from_str(&res_data_str).unwrap();
+//             let position_info: Vec<&SwapPosition> = data_list.iter().filter(|item| item.inst_id == symbol_format).collect();
+//             let result = position_info.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 res_data = self.request.get_positions("SWAP".to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let data_list: Vec<SwapPosition> = serde_json::from_str(&res_data_str).unwrap();
+//             let result = data_list.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_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
+//         let res_data = self.request.get_ticker(symbol_format.clone()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let ticker_info_list: Vec<SwapTicker> = serde_json::from_str(&res_data_str).unwrap();
+//             let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == symbol_format);
+//             match ticker_info {
+//                 None => {
+//                     error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
+//                     panic!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
+//                 }
+//                 Some(value) => {
+//                     let result = Ticker {
+//                         time: value.ts.parse().unwrap(),
+//                         high: value.high24h,
+//                         low: value.low24h,
+//                         sell: value.ask_px,
+//                         buy: value.bid_px,
+//                         last: value.last,
+//                         volume: value.last_sz,
+//                     };
+//                     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_format = format!("{}-SWAP", utils::format_symbol(symbol.clone(), "-"));
+//         let res_data = self.request.get_ticker(symbol_format.clone()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let ticker_info_list: Vec<SwapTicker> = serde_json::from_str(&res_data_str).unwrap();
+//             let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == symbol_format);
+//             match ticker_info {
+//                 None => {
+//                     error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
+//                     panic!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
+//                 }
+//                 Some(value) => {
+//                     let result = Ticker {
+//                         time: value.ts.parse().unwrap(),
+//                         high: value.high24h,
+//                         low: value.low24h,
+//                         sell: value.ask_px,
+//                         buy: value.bid_px,
+//                         last: value.last,
+//                         volume: value.last_sz,
+//                     };
+//                     Ok(result)
+//                 }
+//             }
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn get_market(&mut self) -> Result<Market, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+//         let res_data = self.request.get_instruments().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<SwapMarket> = serde_json::from_str(res_data_str).unwrap();
+//             let market_info = res_data_json.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format));
+//             match market_info {
+//                 None => {
+//                     error!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
+//                     panic!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
+//                 }
+//                 Some(value) => {
+//                     println!("{:?}", value);
+//                     let symbol_array: Vec<&str> = value.inst_family.split("-").collect();
+//                     let result = Market {
+//                         symbol: format!("{}_{}", symbol_array[0], symbol_array[1]),
+//                         base_asset: symbol_array[0].to_string(),
+//                         quote_asset: symbol_array[1].to_string(),
+//                         tick_size: value.tick_sz,
+//                         amount_size: value.min_sz * value.ct_val,
+//                         price_precision: Decimal::from_u32(value.tick_sz.scale()).unwrap(),
+//                         amount_precision: Decimal::from_u32((value.min_sz * value.ct_val).scale()).unwrap(),
+//                         min_qty: value.min_sz,
+//                         max_qty: value.max_lmt_sz,
+//                         min_notional: value.min_sz * value.ct_val,
+//                         max_notional: value.max_lmt_sz * value.ct_val,
+//                         ct_val: value.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_format = utils::format_symbol(symbol.clone(), "-");
+//         let res_data = self.request.get_instruments().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<SwapMarket> = serde_json::from_str(res_data_str).unwrap();
+//             let market_info = res_data_json.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format));
+//             match market_info {
+//                 None => {
+//                     error!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
+//                     panic!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
+//                 }
+//                 Some(value) => {
+//                     let symbol_array: Vec<&str> = value.inst_family.split("-").collect();
+//                     let result = Market {
+//                         symbol: format!("{}_{}", symbol_array[0], symbol_array[1]),
+//                         base_asset: symbol_array[0].to_string(),
+//                         quote_asset: symbol_array[1].to_string(),
+//                         tick_size: value.tick_sz,
+//                         amount_size: value.min_sz * value.ct_val,
+//                         price_precision: Decimal::from_u32(value.tick_sz.scale()).unwrap(),
+//                         amount_precision: Decimal::from_u32((value.min_sz * value.ct_val).scale()).unwrap(),
+//                         min_qty: value.min_sz,
+//                         max_qty: value.max_lmt_sz,
+//                         min_notional: value.min_sz * value.ct_val,
+//                         max_notional: value.max_lmt_sz * value.ct_val,
+//                         ct_val: value.ct_mult,
+//                     };
+//                     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_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
+//         let ct_val = self.market.ct_val;
+//         let res_data = self.request.get_order(symbol_format, order_id.to_string(), custom_id.to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<SwapOrder> = serde_json::from_str(res_data_str).unwrap();
+//             let order_info = res_data_json[0].clone();
+//             let result = format_order_item(order_info, 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_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
+//         let ct_val = self.market.ct_val;
+//         let res_data = self.request.get_incomplete_order(symbol_format.clone()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<SwapOrder> = serde_json::from_str(res_data_str).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 take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+//         let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
+//         let ct_val = self.market.ct_val;
+//         let mut params = json!({
+//             "tdMode": "cross",
+//             "clOrdId": custom_id.to_string(),
+//             "instId": symbol_format,
+//             "px": price.to_string(),
+//         });
+//         params["ordType"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
+//         let size = (amount / ct_val).floor();
+//         params["sz"] = json!(size);
+//         match origin_side {
+//             "kd" => {
+//                 params["side"] = json!("buy");
+//                 params["posSide"] = json!("long");
+//             }
+//             "pd" => {
+//                 params["side"] = json!("sell");
+//                 params["posSide"] = json!("long");
+//             }
+//             "kk" => {
+//                 params["side"] = json!("sell");
+//                 params["posSide"] = json!("short");
+//             }
+//             "pk" => {
+//                 params["side"] = json!("buy");
+//                 params["posSide"] = json!("short");
+//             }
+//             _ => { error!("下单参数错误"); }
+//         };
+//         let res_data = self.request.swap_order(params).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             let order_info = res_data_json[0].clone();
+//             let id = order_info["ordId"].as_str().unwrap().to_string();
+//             let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
+//             let result = Order {
+//                 id,
+//                 custom_id,
+//                 price,
+//                 amount,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "NEW".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("799 okx_swap".to_string()),
+//             };
+//             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_format = format!("{}-SWAP", utils::format_symbol(symbol.clone(), "-"));
+//         let mut params = json!({
+//             "tdMode": "cross",
+//             "clOrdId": custom_id.to_string(),
+//             "instId": symbol_format,
+//             "px": price.to_string(),
+//         });
+//         params["ordType"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
+//         let size = (amount / ct_val).floor();
+//         params["sz"] = json!(size);
+//         match origin_side {
+//             "kd" => {
+//                 params["side"] = json!("buy");
+//                 params["posSide"] = json!("long");
+//             }
+//             "pd" => {
+//                 params["side"] = json!("sell");
+//                 params["posSide"] = json!("long");
+//             }
+//             "kk" => {
+//                 params["side"] = json!("sell");
+//                 params["posSide"] = json!("short");
+//             }
+//             "pk" => {
+//                 params["side"] = json!("buy");
+//                 params["posSide"] = json!("short");
+//             }
+//             _ => { error!("下单参数错误"); }
+//         };
+//         let res_data = self.request.swap_order(params).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             let order_info = res_data_json[0].clone();
+//             let id = order_info["ordId"].as_str().unwrap().to_string();
+//             let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
+//             let result = Order {
+//                 id,
+//                 custom_id,
+//                 price,
+//                 amount,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "NEW".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("853 okx_swap".to_string()),
+//             };
+//             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_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
+//         let res_data = self.request.cancel_order(symbol_format, order_id.to_string(), custom_id.to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+//             let order_info = res_data_json[0].clone();
+//             let id = order_info["ordId"].as_str().unwrap().to_string();
+//             let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
+//             let result = Order {
+//                 id,
+//                 custom_id,
+//                 price: Decimal::ZERO,
+//                 amount: Decimal::ZERO,
+//                 deal_amount: Decimal::ZERO,
+//                 avg_price: Decimal::ZERO,
+//                 status: "REMOVE".to_string(),
+//                 order_type: "".to_string(),
+//                 trace_stack: TraceStack::new(0, Instant::now()).on_special("879 okx_swap".to_string()),
+//             };
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+//         let res_data = self.request.set_position_mode().await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let result = res_data_str.clone();
+//             Ok(result)
+//         } else {
+//             Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//         }
+//     }
+//
+//     async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
+//         let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
+//         let res_data = self.request.set_leverage(symbol_format, leverage.to_string()).await;
+//         if res_data.code == "200" {
+//             let res_data_str = &res_data.data;
+//             let result = res_data_str.clone();
+//             Ok(result)
+//         } 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, "okx_swap:该交易所方法未实现".to_string())) }
+//
+//     async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string())) }
+//
+//     async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
+//         let mut handles = vec![];
+//         // 撤销订单
+//         let cancel = order_command.cancel;
+//         for item in cancel.keys() {
+//             let mut self_clone = self.clone();
+//             let cancel_clone = cancel.clone();
+//             let item_clone = item.clone();
+//             let order_id = cancel_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+//             let custom_id = cancel_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let handle = tokio::spawn(async move {
+//                 if order_id != "" {
+//                     let result = self_clone.cancel_order(&order_id, &custom_id).await;
+//                     match result {
+//                         Ok(_) => {
+//                             // result_sd.send(result).await.unwrap();
+//                         }
+//                         Err(error) => {
+//                             // 取消失败去查订单。
+//                             let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+//                             match query_rst {
+//                                 Ok(order) => {
+//                                     result_sd.send(order).await.unwrap();
+//                                 }
+//                                 Err(query_err) => {
+//                                     error!(?query_err);
+//                                     error!("撤单失败,而且查单也失败了,okx_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+//                                 }
+//                             }
+//                             err_sd.send(error).await.unwrap();
+//                         }
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//         // 下单指令
+//         let mut limits = HashMap::new();
+//         limits.extend(order_command.limits_open);
+//         limits.extend(order_command.limits_close);
+//         for item in limits.keys() {
+//             let mut self_clone = self.clone();
+//             let limits_clone = limits.clone();
+//             let item_clone = item.clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let ts = trace_stack.clone();
+//
+//             let handle = tokio::spawn(async move {
+//                 let value = limits_clone[&item_clone].clone();
+//                 let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
+//                 let side = value.get(1).unwrap();
+//                 let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
+//                 let cid = value.get(3).unwrap();
+//
+//                 //  order_name: [数量,方向,价格,c_id]
+//                 let result = self_clone.take_order(cid, side, price, amount).await;
+//                 match result {
+//                     Ok(mut result) => {
+//                         // ts.on_after_send();
+//                         result.trace_stack = ts.clone();
+//
+//                         result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         let mut err_order = Order::new();
+//                         err_order.custom_id = cid.clone();
+//                         err_order.status = "REMOVE".to_string();
+//
+//                         result_sd.send(err_order).await.unwrap();
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//         // 检查订单指令
+//         let check = order_command.check;
+//         for item in check.keys() {
+//             let mut self_clone = self.clone();
+//             let check_clone = check.clone();
+//             let item_clone = item.clone();
+//             let order_id = check_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+//             let custom_id = check_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+//             let result_sd = self.order_sender.clone();
+//             let err_sd = self.error_sender.clone();
+//             let handle = tokio::spawn(async move {
+//                 let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+//                 match result {
+//                     Ok(result) => {
+//                         result_sd.send(result).await.unwrap();
+//                     }
+//                     Err(error) => {
+//                         err_sd.send(error).await.unwrap();
+//                     }
+//                 }
+//             });
+//             handles.push(handle)
+//         }
+//
+//         let futures = FuturesUnordered::from_iter(handles);
+//         let _: Result<Vec<_>, _> = futures.try_collect().await;
+//     }
+// }
+//
+// pub fn format_order_item(data: SwapOrder, ct_val: Decimal) -> Order {
+//     debug!("format-order-start, okx_swap");
+//     debug!(?data);
+//     let custom_status = if ["canceled", "filled", "mmp_canceled"].contains(&data.state.as_str()) {
+//         "REMOVE".to_string()
+//     } else if ["live", "partially_filled"].contains(&data.state.as_str()) {
+//         "NEW".to_string()
+//     } else {
+//         "NULL".to_string()
+//     };
+//
+//     let result = Order {
+//         id: data.ord_id,
+//         custom_id: data.cl_ord_id,
+//         price: data.px,
+//         amount: data.sz * ct_val,
+//         deal_amount: data.acc_fill_sz * ct_val,
+//         avg_price: if data.avg_px != "" { Decimal::from_str(&data.avg_px).unwrap() } else { Decimal::ZERO },
+//         status: custom_status,
+//         order_type: data.ord_type,
+//         trace_stack: TraceStack::new(0, Instant::now()).on_special("1049 okx_swap".to_string()),
+//     };
+//     debug!(?result);
+//     debug!("format-order-end, okx_swap");
+//     result
+// }
+//
+// pub fn format_position_item(value: &SwapPosition, ct_val: Decimal) -> Position {
+//     let position_mode = match value.pos_side.as_str() {
+//         "long" => { PositionModeEnum::Long }
+//         "short" => { PositionModeEnum::Short }
+//         _ => { PositionModeEnum::Both }
+//     };
+//     Position {
+//         symbol: value.inst_id.replace("-SWAP", ""),
+//         margin_level: value.lever,
+//         amount: value.pos * ct_val,
+//         frozen_amount: Decimal::ZERO,
+//         price: value.avg_px,
+//         profit: value.upl,
+//         position_mode,
+//         margin: if value.margin != "" { Decimal::from_str(&value.margin).unwrap() } else { Decimal::ZERO },
+//     }
+// }

+ 49 - 0
standard/src/utils.rs

@@ -0,0 +1,49 @@
+use tracing::trace;
+use exchanges::proxy;
+use crate::exchange::ExchangeEnum;
+
+/// 修改交易对连接符号
+/// - `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!("检测有代理配置,配置走代理");
+    }
+}
+
+/// 币种映射器
+#[allow(dead_code)]
+pub fn symbol_enter_mapper(exchange_enum: ExchangeEnum, symbol: &str) -> String {
+    let symbol_upper = symbol.to_uppercase();
+    match exchange_enum {
+        ExchangeEnum::KucoinSwap => {
+            if symbol_upper.contains("BTC") {
+                symbol_upper.replace("BTC", "XBT")
+            } else { symbol_upper.to_string() }
+        }
+        _ => {
+            symbol_upper.to_string()
+        }
+    }
+}
+
+/// 币种映射器
+#[allow(dead_code)]
+pub fn symbol_out_mapper(exchange_enum: ExchangeEnum, symbol: &str) -> String {
+    let symbol_upper = symbol.to_uppercase();
+    match exchange_enum {
+        ExchangeEnum::KucoinSwap => {
+            if symbol_upper.contains("XBT") {
+                symbol_upper.replace("XBT", "BTC")
+            } else { symbol_upper.to_string() }
+        }
+        _ => {
+            symbol_upper.to_string()
+        }
+    }
+}

+ 31 - 0
standard/tests/binance_handle_test.rs

@@ -0,0 +1,31 @@
+mod exchange_test;
+use tracing::instrument;
+use exchanges::binance_swap_ws::BinanceSwapSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "BTC_USDT";
+
+// 测试订阅深度信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_subscribe_type = vec![
+        BinanceSwapSubscribeType::PuDepth20levels100ms,
+    ];
+    test_new_exchange_wss(ExchangeEnum::BinanceSwap, SYMBOL, binance_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 binance_subscribe_type = vec![
+        BinanceSwapSubscribeType::PuBookTicker,
+    ];
+    test_new_exchange_wss(ExchangeEnum::BinanceSwap, SYMBOL, binance_subscribe_type, "ticker").await;
+}

+ 31 - 0
standard/tests/binance_spot_handle_test.rs

@@ -0,0 +1,31 @@
+mod exchange_test;
+use tracing::instrument;
+use exchanges::binance_spot_ws::BinanceSpotSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "BTC_USDT";
+
+// 测试订阅深度信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_subscribe_type = vec![
+        BinanceSpotSubscribeType::PuDepth20levels100ms,
+    ];
+    test_new_exchange_wss(ExchangeEnum::BinanceSpot, SYMBOL, binance_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 binance_subscribe_type = vec![
+        BinanceSpotSubscribeType::PuBookTicker,
+    ];
+    test_new_exchange_wss(ExchangeEnum::BinanceSpot, SYMBOL, binance_subscribe_type, "ticker").await;
+}

+ 172 - 0
standard/tests/binance_swap_test.rs

@@ -0,0 +1,172 @@
+mod exchange_test;
+
+use tracing::{instrument, trace};
+use standard::exchange::{ExchangeEnum};
+use standard::{Platform};
+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 binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_self_exchange = binance_swap_exchange.get_self_exchange();
+    trace!(?binance_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_self_symbol = binance_swap_exchange.get_self_symbol();
+    trace!(?binance_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_self_is_colo = binance_swap_exchange.get_self_is_colo();
+    trace!(?binance_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_self_params = binance_swap_exchange.get_self_params();
+    trace!("binance_get_self_params={:?}",binance_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_self_market = binance_swap_exchange.get_self_market();
+    trace!(?binance_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_request_delays = binance_swap_exchange.get_request_delays();
+    trace!(?binance_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_request_avg_delay = binance_swap_exchange.get_request_avg_delay();
+    trace!(?binance_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_request_max_delay = binance_swap_exchange.get_request_max_delay();
+    trace!(?binance_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_server_time = binance_swap_exchange.get_server_time().await;
+    trace!(?binance_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_account = binance_swap_exchange.get_account().await;
+    trace!(?binance_get_account);
+}
+
+// 测试获取持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_position() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_position = binance_swap_exchange.get_position().await;
+    trace!(?binance_get_position);
+}
+
+// 测试获取所有持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_positions() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_position = binance_swap_exchange.get_positions().await;
+    trace!(?binance_get_position);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_ticker = binance_swap_exchange.get_ticker().await;
+    trace!(?binance_get_ticker);
+}
+
+// 测试获取订单详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_order_detail() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_order_detail = binance_swap_exchange.get_order_detail("", "9999").await;
+    trace!(?binance_get_order_detail);
+}
+// 测试获取订单详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_orders_list() {
+    global::log_utils::init_log_with_trace();
+
+    let mut binance_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BinanceSwap, SYMBOL).await;
+    let binance_get_orders_list = binance_swap_exchange.get_orders_list("open").await;
+    trace!(?binance_get_orders_list);
+}

+ 56 - 0
standard/tests/bitget_spot_handle_test.rs

@@ -0,0 +1,56 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::bitget_spot_ws::{BitgetSpotSubscribeType};
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "BLZ_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_subscribe_type = vec![
+        BitgetSpotSubscribeType::PuBooks5
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_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 bitget_subscribe_type = vec![
+        BitgetSpotSubscribeType::PuTicker
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_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 bitget_subscribe_type = vec![
+        BitgetSpotSubscribeType::PrAccount
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_subscribe_type, "account").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 bitget_subscribe_type = vec![
+        BitgetSpotSubscribeType::PrOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_subscribe_type, "orders").await;
+}

+ 195 - 0
standard/tests/bitget_spot_test.rs

@@ -0,0 +1,195 @@
+use rust_decimal_macros::dec;
+use tracing::{instrument, trace};
+use standard::exchange::ExchangeEnum;
+use standard::Platform;
+use crate::exchange_test::test_new_exchange;
+
+mod exchange_test;
+
+const SYMBOL: &str = "BLZ_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_self_exchange = bitget_swap_exchange.get_self_exchange();
+    trace!(?bitget_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_self_symbol = bitget_swap_exchange.get_self_symbol();
+    trace!(?bitget_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_self_is_colo = bitget_swap_exchange.get_self_is_colo();
+    trace!(?bitget_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_self_params = bitget_swap_exchange.get_self_params();
+    trace!("bitget_get_self_params={:?}",bitget_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_self_market = bitget_swap_exchange.get_self_market();
+    trace!(?bitget_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_request_delays = bitget_swap_exchange.get_request_delays();
+    trace!(?bitget_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_request_avg_delay = bitget_swap_exchange.get_request_avg_delay();
+    trace!(?bitget_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_request_max_delay = bitget_swap_exchange.get_request_max_delay();
+    trace!(?bitget_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_server_time = bitget_swap_exchange.get_server_time().await;
+    trace!(?bitget_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_spot_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_spot_account = bitget_swap_exchange.get_spot_account().await;
+    trace!(?bitget_get_spot_account);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_ticker = bitget_swap_exchange.get_ticker().await;
+    trace!(?bitget_get_ticker);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_market() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_market = bitget_swap_exchange.get_market().await;
+    trace!(?bitget_get_market);
+}
+
+// 测试获取Order详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_order_detail() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_order_detail = bitget_swap_exchange.get_order_detail("", "999998").await;
+    trace!(?bitget_get_order_detail);
+}
+
+// 测试获取Order列表信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_orders_list() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_get_orders_list = bitget_swap_exchange.get_orders_list("").await;
+    trace!(?bitget_get_orders_list);
+}
+
+// 测试下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_take_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_take_order = bitget_swap_exchange.take_order("9999922", "kd", dec!(0.21), dec!(30)).await;
+    trace!(?bitget_take_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_cancel_order = bitget_swap_exchange.cancel_order("", "999997").await;
+    trace!(?bitget_cancel_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let mut bitget_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::BitgetSpot, SYMBOL).await;
+    let bitget_cancel_order = bitget_swap_exchange.cancel_orders().await;
+    trace!(?bitget_cancel_order);
+}

+ 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_label(name, false, Option::from(params), BinanceSpotWsType::PublicAndPrivate);
+            // exchange_wss.set_symbols(vec![symbol_format]);
+            // exchange_wss.set_subscribe(subscriber_type.into());
+            //
+            //
+            // let mold_arc = Arc::new(mold.to_string());
+            // //读取
+            // tokio::spawn(async move {
+            //     let mold_clone = Arc::clone(&mold_arc);
+            //     loop {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if data.data != "" {
+            //                         let result = binance_spot_handle::handle_special_depth(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if data.data != "" {
+            //                         let result = binance_spot_handle::handle_special_ticker(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     };
+            // });
+            //
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
+        }
+        ExchangeEnum::BinanceSwap => {
+            // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
+            // trace!(symbol_format);
+            // 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_label(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_label(name, false, Option::from(params), KucoinSwapWsType::Public).await;
+            // } else {
+            //     exchange_wss = KucoinSwapWs::new_label(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_label(name, false, Option::from(params), KucoinSpotWsType::Public).await
+            // } else {
+            //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Private).await
+            // };
+            // exchange_wss.set_symbols(vec![symbol_format]);
+            // exchange_wss.set_subscribe(subscriber_type.into());
+            //
+            // let mold_arc = Arc::new(mold.to_string());
+            // tokio::spawn(async move {
+            //     let mold_clone = Arc::clone(&mold_arc);
+            //     loop {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_special_depth(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_special_ticker(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "account" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_account_info(data, symbol_back.clone());
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "orders" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_order(data, dec!(1));
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     }
+            // });
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
+        }
+        ExchangeEnum::GateSwap => {
+            // let symbol_format = utils::format_symbol(symbol.to_string(), "_").to_uppercase();
+            // trace!(symbol_format);
+            // let name = format!("gate_swap@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
+            // let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+            //
+            // let params = GateSwapLogin {
+            //     api_key: account_info.gate_access_key,
+            //     secret: account_info.gate_secret_key,
+            // };
+            // let mut exchange_wss = GateSwapWs::new_label(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_label(name, false, Option::from(params), BitgetSpotWsType::Public)
+            // } else {
+            //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Private)
+            // };
+            // exchange_wss.set_symbols(vec![symbol_format]);
+            // exchange_wss.set_subscribe(subscriber_type.into());
+            //
+            // let mold_arc = Arc::new(mold.to_string());
+            // //读取
+            // tokio::spawn(async move {
+            //     loop {
+            //         let mold_clone = Arc::clone(&mold_arc);
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_special_depth(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_special_ticker(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "account" => {
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_account_info(data, symbol_back.clone());
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "orders" => {
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_order(data, dec!(1));
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     }
+            // });
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
+        }
+        ExchangeEnum::OkxSwap => {
+            let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+            trace!(symbol_format);
+            let name = format!("okx_swap@{}", symbol.to_string().to_lowercase());
+            let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            let write_tx_am = Arc::new(Mutex::new(write_tx));
+            let is_shutdown_arc = Arc::new(AtomicBool::new(true));
+
+            let params = OkxSwapLogin {
+                api_key: account_info.okx_access_key,
+                secret_key: account_info.okx_secret_key,
+                passphrase: account_info.okx_pass,
+            };
+
+            let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
+                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Public)
+            } else if ["account", "orders", "position"].contains(&mold) {
+                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Private)
+            } else {
+                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Business)
+            };
+
+            exchange_wss.set_symbols(vec![symbol_format.clone()]);
+            exchange_wss.set_subscribe(subscriber_type.into());
+
+            let mold_arc = Arc::new(mold.to_string());
+            tokio::spawn(async move {
+                let mold_clone = Arc::clone(&mold_arc);
+                loop {
+                    if let Some(data) = read_rx.next().await {
+                        trace!("原始数据 data:{:?}",data);
+                        match mold_clone.as_str() {
+                            "depth" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_special_depth(data);
+                                    trace!(?result)
+                                }
+                            }
+                            "ticker" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_special_ticker(data);
+                                    trace!(?result)
+                                }
+                            }
+                            "account" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_account_info(data, symbol_format.clone());
+                                    trace!(?result)
+                                }
+                            }
+                            "position" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_position(data, dec!(10));
+                                    trace!(?result)
+                                }
+                            }
+                            "orders" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_order(data, dec!(10));
+                                    trace!(?result)
+                                }
+                            }
+                            _ => {
+                                error!("没有该命令!mode={}", mold_clone);
+                                panic!("没有该命令!mode={}", mold_clone)
+                            }
+                        }
+                    }
+                }
+            });
+
+            let t1 = tokio::spawn(async move {
+                //链接
+                let bool_v3_clone = Arc::clone(&is_shutdown_arc);
+                exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            });
+            try_join!(t1).unwrap();
+        }
+        _ => {
+            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);
+        }
+    }
+}

+ 68 - 0
standard/tests/kucoin_handle_test.rs

@@ -0,0 +1,68 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::kucoin_swap_ws::{KucoinSwapSubscribeType};
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "BLZ_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_subscribe_type = vec![
+        KucoinSwapSubscribeType::PuContractMarketLevel2Depth50
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_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 kucoin_subscribe_type = vec![
+        KucoinSwapSubscribeType::PuContractMarkettickerV2
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_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 kucoin_subscribe_type = vec![
+        KucoinSwapSubscribeType::PrContractAccountWallet
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_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 kucoin_subscribe_type = vec![
+        KucoinSwapSubscribeType::PrContractPosition
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_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 kucoin_subscribe_type = vec![
+        KucoinSwapSubscribeType::PrContractMarketTradeOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_subscribe_type, "orders").await;
+}

+ 56 - 0
standard/tests/kucoin_spot_handle_test.rs

@@ -0,0 +1,56 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::kucoin_spot_ws::{KucoinSpotSubscribeType};
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "BTC_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_subscribe_type = vec![
+        KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_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 kucoin_subscribe_type = vec![
+        KucoinSpotSubscribeType::PuMarketTicker
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_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 kucoin_subscribe_type = vec![
+        KucoinSpotSubscribeType::PrAccountBalance
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_subscribe_type, "account").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 kucoin_subscribe_type = vec![
+        KucoinSpotSubscribeType::PrSpotMarketTradeOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_subscribe_type, "orders").await;
+}

+ 145 - 0
standard/tests/kucoin_spot_test.rs

@@ -0,0 +1,145 @@
+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 kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_self_exchange = kucoin_spot_exchange.get_self_exchange();
+    trace!(?kucoin_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_self_symbol = kucoin_spot_exchange.get_self_symbol();
+    trace!(?kucoin_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_self_is_colo = kucoin_spot_exchange.get_self_is_colo();
+    trace!(?kucoin_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_self_params = kucoin_spot_exchange.get_self_params();
+    trace!("kucoin_get_self_params={:?}",kucoin_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_self_market = kucoin_spot_exchange.get_self_market();
+    trace!(?kucoin_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_request_delays = kucoin_spot_exchange.get_request_delays();
+    trace!(?kucoin_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_request_avg_delay = kucoin_spot_exchange.get_request_avg_delay();
+    trace!(?kucoin_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_request_max_delay = kucoin_spot_exchange.get_request_max_delay();
+    trace!(?kucoin_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_server_time = kucoin_spot_exchange.get_server_time().await;
+    trace!(?kucoin_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_account = kucoin_spot_exchange.get_account().await;
+    trace!(?kucoin_get_account);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_ticker = kucoin_spot_exchange.get_ticker().await;
+    trace!(?kucoin_get_ticker);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_market() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_spot_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSpot, SYMBOL).await;
+    let kucoin_get_market = kucoin_spot_exchange.get_market().await;
+    trace!(?kucoin_get_market);
+}

+ 271 - 0
standard/tests/kucoin_swap_test.rs

@@ -0,0 +1,271 @@
+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 = "BTC_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_self_exchange = kucoin_swap_exchange.get_self_exchange();
+    trace!(?kucoin_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_self_symbol = kucoin_swap_exchange.get_self_symbol();
+    trace!(?kucoin_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_self_is_colo = kucoin_swap_exchange.get_self_is_colo();
+    trace!(?kucoin_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_self_params = kucoin_swap_exchange.get_self_params();
+    trace!("kucoin_get_self_params={:?}",kucoin_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_self_market = kucoin_swap_exchange.get_self_market();
+    trace!(?kucoin_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_request_delays = kucoin_swap_exchange.get_request_delays();
+    trace!(?kucoin_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_request_avg_delay = kucoin_swap_exchange.get_request_avg_delay();
+    trace!(?kucoin_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_request_max_delay = kucoin_swap_exchange.get_request_max_delay();
+    trace!(?kucoin_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_server_time = kucoin_swap_exchange.get_server_time().await;
+    trace!(?kucoin_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_account = kucoin_swap_exchange.get_account().await;
+    trace!(?kucoin_get_account);
+}
+
+// 测试获取持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_position() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_position = kucoin_swap_exchange.get_position().await;
+    trace!(?kucoin_get_position);
+}
+
+// 测试获取所有持仓信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_positions() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_positions = kucoin_swap_exchange.get_positions().await;
+    trace!(?kucoin_get_positions);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_ticker = kucoin_swap_exchange.get_ticker().await;
+    trace!(?kucoin_get_ticker);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_market() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_market = kucoin_swap_exchange.get_market().await;
+    trace!(?kucoin_get_market);
+}
+
+// 测试获取Order详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_order_detail() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_order_detail = kucoin_swap_exchange.get_order_detail("", "999999").await;
+    trace!(?kucoin_get_order_detail);
+}
+
+// 测试获取Order列表信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_orders_list() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_get_orders_list = kucoin_swap_exchange.get_orders_list("active").await;
+    trace!(?kucoin_get_orders_list);
+}
+
+// 测试下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_take_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_take_order = kucoin_swap_exchange.take_order("999999", "kk", dec!(0), dec!(100)).await;
+    trace!(?kucoin_take_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_cancel_order = kucoin_swap_exchange.cancel_order("108721685804371970", "").await;
+    trace!(?kucoin_cancel_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_cancel_orders = kucoin_swap_exchange.cancel_orders().await;
+    trace!(?kucoin_cancel_orders);
+}
+
+
+// 测试设置自动追加保证金
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_auto_deposit_status() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_set_auto_deposit_status = kucoin_swap_exchange.set_auto_deposit_status(true).await;
+    trace!(?kucoin_set_auto_deposit_status);
+}
+
+// 测试指令下单
+#[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("kucoin_access_key").unwrap_or("".to_string());
+    let secret_key = env::var("kucoin_secret_key").unwrap_or("".to_string());
+    let pass_key = env::var("kucoin_pass_key").unwrap_or("".to_string());
+    params.insert("access_key".to_string(), access_key);
+    params.insert("secret_key".to_string(), secret_key);
+    params.insert("pass_key".to_string(), pass_key);
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = Exchange::new(ExchangeEnum::KucoinSwap, 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(), "94647166466789377".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(), "94647166466789377".to_string()]);
+    kucoin_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);
+        }
+    }
+}

+ 68 - 0
standard/tests/okx_handle_test.rs

@@ -0,0 +1,68 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::okx_swap_ws::OkxSwapSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "BTC_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSwapSubscribeType::PuBooks5
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_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 okx_subscribe_type = vec![
+        OkxSwapSubscribeType::PuBooks5
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_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 okx_subscribe_type = vec![
+        OkxSwapSubscribeType::PrAccount("USDT".to_string())
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_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 okx_subscribe_type = vec![
+        OkxSwapSubscribeType::PrPositions
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_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 okx_subscribe_type = vec![
+        OkxSwapSubscribeType::PrOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "orders").await;
+}

+ 229 - 0
standard/tests/okx_swap_test.rs

@@ -0,0 +1,229 @@
+use rust_decimal_macros::dec;
+use tracing::{instrument, trace};
+use standard::exchange::ExchangeEnum;
+use standard::Platform;
+use crate::exchange_test::test_new_exchange;
+
+mod exchange_test;
+
+const SYMBOL: &str = "CRO_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_exchange = okx_swap_exchange.get_self_exchange();
+    trace!(?okx_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_symbol = okx_swap_exchange.get_self_symbol();
+    trace!(?okx_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_is_colo = okx_swap_exchange.get_self_is_colo();
+    trace!(?okx_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_params = okx_swap_exchange.get_self_params();
+    trace!("okx_get_self_params={:?}",okx_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_market = okx_swap_exchange.get_self_market();
+    trace!(?okx_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_request_delays = okx_swap_exchange.get_request_delays();
+    trace!(?okx_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_request_avg_delay = okx_swap_exchange.get_request_avg_delay();
+    trace!(?okx_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_request_max_delay = okx_swap_exchange.get_request_max_delay();
+    trace!(?okx_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_server_time = okx_swap_exchange.get_server_time().await;
+    trace!(?okx_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_account = okx_swap_exchange.get_account().await;
+    trace!(?okx_get_account);
+}
+
+// 测试获取仓位信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_position() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_position = okx_swap_exchange.get_position().await;
+    trace!(?okx_get_position);
+}
+
+// 测试获取Ticker信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_ticker = okx_swap_exchange.get_ticker().await;
+    trace!(?okx_get_ticker);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_market() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_market = okx_swap_exchange.get_market().await;
+    trace!(?okx_get_market);
+}
+
+// 测试获取Order详情信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_order_detail() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_order_detail = okx_swap_exchange.get_order_detail("", "999997").await;
+    trace!(?okx_get_order_detail);
+}
+
+// 测试获取Order列表信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_orders_list() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_orders_list = okx_swap_exchange.get_orders_list("active").await;
+    trace!(?okx_get_orders_list);
+}
+
+// 测试下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_take_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_take_order = okx_swap_exchange.take_order("999997", "kk", dec!(0.0901), dec!(100)).await;
+    trace!(?okx_take_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_cancel_order = okx_swap_exchange.cancel_order("", "999998").await;
+    trace!(?okx_cancel_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_cancel_orders = kucoin_swap_exchange.cancel_orders().await;
+    trace!(?kucoin_cancel_orders);
+}
+
+// 测试设置持仓模式
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_dual_mode() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_set_dual_mode = okx_swap_exchange.set_dual_mode("usdt", true).await;
+    trace!(?okx_set_dual_mode);
+}
+
+// 测试设置杠杆
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_dual_leverage() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_set_dual_leverage = okx_swap_exchange.set_dual_leverage("10").await;
+    trace!(?okx_set_dual_leverage);
+}