skyfffire 3 months ago
commit
c5bbfedc50
100 changed files with 22312 additions and 0 deletions
  1. 13 0
      .gitignore
  2. 40 0
      Cargo.toml
  3. 72 0
      README.md
  4. 3 0
      derive/.gitignore
  5. 21 0
      derive/Cargo.toml
  6. 102 0
      derive/src/binance_swap_export.rs
  7. 78 0
      derive/src/bitget_spot_export.rs
  8. 84 0
      derive/src/bitget_swap_export.rs
  9. 201 0
      derive/src/bybit_swap_export.rs
  10. 56 0
      derive/src/export_excel.rs
  11. 100 0
      derive/src/gate_swap_export.rs
  12. 104 0
      derive/src/kucoin_spot_export.rs
  13. 110 0
      derive/src/kucoin_swap_export.rs
  14. 16 0
      derive/src/lib.rs
  15. 120 0
      derive/src/okx_swap_export.rs
  16. 2 0
      exchanges/.gitignore
  17. 52 0
      exchanges/Cargo.toml
  18. 32 0
      exchanges/README.md
  19. 14 0
      exchanges/src/binance_spot_rest.rs
  20. 255 0
      exchanges/src/binance_spot_ws.rs
  21. 533 0
      exchanges/src/binance_swap_rest.rs
  22. 466 0
      exchanges/src/binance_swap_ws.rs
  23. 314 0
      exchanges/src/bingx_swap_rest.rs
  24. 318 0
      exchanges/src/bingx_swap_ws.rs
  25. 825 0
      exchanges/src/bitget_spot_rest.rs
  26. 346 0
      exchanges/src/bitget_spot_ws.rs
  27. 420 0
      exchanges/src/bitget_swap_rest.rs
  28. 351 0
      exchanges/src/bitget_swap_ws.rs
  29. 399 0
      exchanges/src/bitmart_swap_rest.rs
  30. 459 0
      exchanges/src/bitmart_swap_ws.rs
  31. 509 0
      exchanges/src/bybit_swap_rest.rs
  32. 353 0
      exchanges/src/bybit_swap_ws.rs
  33. 696 0
      exchanges/src/coinex_swap_rest.rs
  34. 414 0
      exchanges/src/coinex_swap_ws.rs
  35. 313 0
      exchanges/src/coinsph_swap_rest.rs
  36. 400 0
      exchanges/src/coinsph_swap_ws.rs
  37. 307 0
      exchanges/src/cointr_swap_rest.rs
  38. 434 0
      exchanges/src/cointr_swap_ws.rs
  39. 285 0
      exchanges/src/crypto_spot_ws.rs
  40. 15 0
      exchanges/src/gate_spot_rest.rs
  41. 15 0
      exchanges/src/gate_spot_ws.rs
  42. 615 0
      exchanges/src/gate_swap_rest.rs
  43. 365 0
      exchanges/src/gate_swap_ws.rs
  44. 102 0
      exchanges/src/http_tool.rs
  45. 508 0
      exchanges/src/htx_swap_rest.rs
  46. 467 0
      exchanges/src/htx_swap_ws.rs
  47. 521 0
      exchanges/src/kucoin_spot_rest.rs
  48. 378 0
      exchanges/src/kucoin_spot_ws.rs
  49. 625 0
      exchanges/src/kucoin_swap_rest.rs
  50. 403 0
      exchanges/src/kucoin_swap_ws.rs
  51. 44 0
      exchanges/src/lib.rs
  52. 314 0
      exchanges/src/mexc_swap_rest.rs
  53. 368 0
      exchanges/src/mexc_swap_ws.rs
  54. 305 0
      exchanges/src/okx_swap_rest.rs
  55. 371 0
      exchanges/src/okx_swap_ws.rs
  56. 314 0
      exchanges/src/phemex_swap_rest.rs
  57. 339 0
      exchanges/src/phemex_swap_ws.rs
  58. 134 0
      exchanges/src/proxy.rs
  59. 50 0
      exchanges/src/response_base.rs
  60. 457 0
      exchanges/src/socket_tool.rs
  61. 9 0
      exchanges/src/utils.rs
  62. 305 0
      exchanges/src/woo_swap_rest.rs
  63. 411 0
      exchanges/src/woo_swap_ws.rs
  64. 92 0
      exchanges/src/xlsx_utils.rs
  65. 5 0
      global/.gitignore
  66. 28 0
      global/Cargo.toml
  67. 69 0
      global/src/account_info.rs
  68. 30 0
      global/src/cci.rs
  69. 133 0
      global/src/clear_log_utils.rs
  70. 26 0
      global/src/clear_position_result.rs
  71. 76 0
      global/src/export_utils.rs
  72. 42 0
      global/src/fixed_time_range_deque.rs
  73. 12 0
      global/src/lib.rs
  74. 106 0
      global/src/log_utils.rs
  75. 117 0
      global/src/params.rs
  76. 25 0
      global/src/predictor_state.rs
  77. 35 0
      global/src/public_params.rs
  78. 162 0
      global/src/trace_stack.rs
  79. 17 0
      global/src/trade.rs
  80. 10 0
      global/tests/get_account_info_test.rs
  81. 467 0
      phemex_swap_rest.rs
  82. 1 0
      rust-toolchain
  83. 36 0
      src/clear_core_libs.rs
  84. 12 0
      src/control_c.rs
  85. 149 0
      src/core_libs.rs
  86. 197 0
      src/main.rs
  87. 126 0
      src/server.rs
  88. 5 0
      standard/.gitignore
  89. 23 0
      standard/Cargo.toml
  90. 15 0
      standard/README.md
  91. 118 0
      standard/src/binance_spot.rs
  92. 51 0
      standard/src/binance_spot_handle.rs
  93. 675 0
      standard/src/binance_swap.rs
  94. 216 0
      standard/src/binance_swap_handle.rs
  95. 259 0
      standard/src/bingx_swap.rs
  96. 105 0
      standard/src/bingx_swap_handle.rs
  97. 625 0
      standard/src/bitget_spot.rs
  98. 136 0
      standard/src/bitget_spot_handle.rs
  99. 791 0
      standard/src/bitget_swap.rs
  100. 268 0
      standard/src/bitget_swap_handle.rs

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+/target
+/.idea
+/db
+
+Cargo.lock
+config.toml*
+*.log
+*.log.*
+/logs*
+/test_account.toml
+
+config.json
+config.json*

+ 40 - 0
Cargo.toml

@@ -0,0 +1,40 @@
+[package]
+name = "as-rust"
+version = "5.0.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+strategy = {path="./strategy"}
+standard = { path="./standard" }
+exchanges = { path="./exchanges" }
+global = { path="./global" }
+tokio = { version = "1.31.0", features = ["full"] }
+chrono = "0.4.26"
+tracing = "0.1"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+tracing-appender-timezone = { git = "https://github.com/skyfffire/tracing-appender-timezone.git" }
+serde = { version = "1.0.188", features = ["derive"] }
+actix-rt = "2.5.0"
+actix-web = "4.0.0-beta.12"
+ctrlc = "3.2.5"
+serde_json = "1.0.105"
+rust_decimal = { version = "1.32.0", features = ["maths"] }
+rust_decimal_macros = "1.32.0"
+actix-cors = "0.6"
+backtrace = "0.3" # 堆栈跟踪
+
+[workspace]
+members=[
+    "exchanges",
+    "standard",
+    "strategy",
+    "global",
+    "derive"
+]
+
+[target.'cfg(any(target_os = "linux"))'.dependencies.openssl]
+version = "*"
+features = ["vendored"]
+

+ 72 - 0
README.md

@@ -0,0 +1,72 @@
+## 声明
+
+
+## 项目结构解析
+
+```
+|
+├─ main                                 // 系统入口
+│
+├─ exchanges                            // 交易所层(网络层)
+│
+├─ strategy                             // 策略层(主逻辑、风控等)
+│
+├─ standard                             // 标准化层(中间件)
+│
+└─ global                               // 一些全局变量或配置
+```
+
+## 运行步骤(以ubuntu 20 为例)
+
+### 1. 安装rust运行环境,安装环境时选择1)Proceed with installation (default)
+```shell
+sudo su
+sudo apt update -y
+sudo apt-get install build-essential -y 
+sudo apt-get install libssl-dev -y 
+sudo apt-get install pkg-config -y 
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+
+```
+然后先后运行这两个命令,检查rust运行环境安装成功:
+```shell
+source $HOME/.cargo/env
+rustc --version
+```
+
+增加虚拟内存
+```shell
+sudo fallocate -l 1G /swapfile
+ls -lh /swapfile
+sudo chmod 600 /swapfile
+sudo mkswap /swapfile
+sudo swapon /swapfile
+free -h
+```
+
+### 2. 项目本地化(第一次使用git需要输入git用户名和密码完成克隆)
+```shell
+cd /
+mkdir app
+cd app
+git config --global credential.helper store
+git clone http://git.skyfffire.com/skyfffire/as-rust.git
+```
+
+## 3. 配置相关参数
+```shell
+cd as-rust/
+cp config.toml.sample config.toml
+nano config.toml
+```
+使用nano文本编辑器时,ctl+s保存,ctl+x退出编辑
+
+## 4. 编译及运行
+```shell
+cargo run
+```
+
+## 5. 如果之后项目有更新,执行下面命令可以获取最新版
+```shell
+git pull origin master
+```

+ 3 - 0
derive/.gitignore

@@ -0,0 +1,3 @@
+/target
+/.idea
+/logs*

+ 21 - 0
derive/Cargo.toml

@@ -0,0 +1,21 @@
+[package]
+name = "derive"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+exchanges = { path = "../exchanges" }
+standard = { path = "../standard" }
+global = { path = "../global" }
+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"] }

+ 102 - 0
derive/src/binance_swap_export.rs

@@ -0,0 +1,102 @@
+use std::collections::{BTreeMap};
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+use rust_decimal::prelude::ToPrimitive;
+use serde::{Deserialize, Serialize};
+use tracing::{info, warn};
+use exchanges::binance_swap_rest::BinanceSwapRest;
+use standard::utils;
+use crate::ExportConnector;
+
+pub struct BinanceSwapExport {
+    request: BinanceSwapRest,
+}
+
+impl BinanceSwapExport {
+    pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> BinanceSwapExport {
+        BinanceSwapExport {
+            request: BinanceSwapRest::new(is_colo, params.clone())
+        }
+    }
+}
+
+/// TradesSwap
+/// - `buyer`: bool, 是否是买方
+/// - `commission`: String, 手续费
+/// - `commission_asset`: String, 手续费计价单位
+/// - `id`: i64, 交易ID
+/// - `maker`: bool, 是否是挂单方
+/// - `order_id`: i64, 订单编号
+/// - `price`: String, 成交价
+/// - `qty`: String, 成交量
+/// - `quote_qty`: String, 成交额
+/// - `realized_pnl`: String, 实现盈亏
+/// - `side`: String, 买卖方向
+/// - `position_side`: String, 持仓方向
+/// - `symbol`: String, 交易对
+/// - `time`: i64 时间
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct TradesSwap {
+    buyer: bool,
+    commission: String,
+    commission_asset: String,
+    id: i64,
+    maker: bool,
+    order_id: i64,
+    price: String,
+    qty: String,
+    quote_qty: String,
+    realized_pnl: String,
+    side: String,
+    position_side: String,
+    symbol: String,
+    time: i64,
+}
+
+#[async_trait]
+impl ExportConnector for BinanceSwapExport {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let limit_params = if limit > 1000 {
+            warn!("查询条数最大为1000条,已修改为1000条!");
+            1000
+        } else { limit };
+        let mut first_time = start_time;
+        let mut data_array = vec![];
+
+        loop {
+            let res_data = self.request.get_user_trades(symbol_format.clone(), first_time, end_time, limit_params).await;
+            info!(?res_data);
+            if res_data.code == 200 {
+                let trades_info: Vec<TradesSwap> = serde_json::from_str(&res_data.data.to_string()).unwrap();
+                for value in trades_info.iter() {
+                    first_time = value.time + 1;
+                    let time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.time.clone()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+                    let order_type = if value.maker { "Maker" } else { "Taker" };
+
+                    data_array.push(vec![
+                        value.id.to_string(),
+                        value.order_id.to_string(),
+                        value.symbol.clone(),
+                        value.side.clone(),
+                        value.price.clone(),
+                        value.qty.clone(),
+                        order_type.to_string(),
+                        value.quote_qty.clone(),
+                        value.commission.clone(),
+                        value.realized_pnl.clone(),
+                        time,
+                    ]);
+                }
+
+                if trades_info.len().to_i64().unwrap() < limit_params {
+                    break;
+                }
+            }
+        }
+
+        let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "交易类型", "成交价值", "交易费用", "交易盈亏", "交易时间"];
+        global::export_utils::export_excel(header_array, data_array, prefix_name)
+    }
+}

+ 78 - 0
derive/src/bitget_spot_export.rs

@@ -0,0 +1,78 @@
+// use std::collections::BTreeMap;
+// use std::str::FromStr;
+// use async_trait::async_trait;
+// use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+// use rust_decimal::Decimal;
+// use serde::{Deserialize, Serialize};
+// use tracing::warn;
+// use exchanges::bitget_spot_rest::BitgetSpotRest;
+// use standard::utils;
+// use crate::ExportConnector;
+//
+// pub struct BitgetSpotExport {
+//     request: BitgetSpotRest,
+// }
+//
+// impl BitgetSpotExport {
+//     pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> BitgetSpotExport {
+//         BitgetSpotExport {
+//             request: BitgetSpotRest::new(is_colo, params.clone())
+//         }
+//     }
+// }
+//
+// /// TradesSwap
+// /// - `symbol`: String, 交易对名称
+// /// - `trade_id`: String, 成交单ID
+// /// - `side`: String, 交易方向
+// /// - `price`: String, 成交价格
+// /// - `size`: String, 成交数量
+// /// - `ts`: String, 成交时间
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct TradesSpot {
+//     symbol: String,
+//     trade_id: String,
+//     side: String,
+//     price: String,
+//     size: String,
+//     ts: String,
+// }
+//
+// #[async_trait]
+// impl ExportConnector for BitgetSpotExport {
+//     async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+//         let symbol_format = utils::format_symbol(symbol, "");
+//         let start_at = if start_time > 0 { start_time.to_string() } else { "".to_string() };
+//         let end_at = if end_time > 0 { end_time.to_string() } else { "".to_string() };
+//         let limit_params = if limit > 1000 {
+//             warn!("查询条数最大为1000条,已修改为1000条!");
+//             1000
+//         } else { limit };
+//         let res_data = self.request.get_market_fills_history(symbol_format, start_at.to_string(), end_at.to_string(), limit_params.to_string()).await;
+//         if res_data.code == "200" {
+//             let trades_info: Vec<TradesSpot> = serde_json::from_str(&res_data.data).unwrap();
+//             let header_array = vec!["交易编号", "交易币对", "买卖方向", "订单价格", "订单数量", "成交额", "创建时间"];
+//             let mut data_array: Vec<Vec<String>> = Vec::new();
+//             for (index, value) in trades_info.iter().enumerate() {
+//                 if index >= data_array.len() {
+//                     data_array.push(Vec::new());
+//                 }
+//                 let created_at = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.ts.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+//                 let trade_value = Decimal::from_str(&value.price).unwrap() * Decimal::from_str(&value.size).unwrap();
+//                 data_array[index] = vec![
+//                     value.trade_id.clone(),
+//                     value.symbol.clone(),
+//                     value.side.clone(),
+//                     value.price.clone(),
+//                     value.size.clone(),
+//                     trade_value.to_string(),
+//                     created_at,
+//                 ];
+//             }
+//             global::export_utils::export_excel(header_array, data_array, prefix_name)
+//         } else {
+//             res_data.to_string()
+//         }
+//     }
+// }

+ 84 - 0
derive/src/bitget_swap_export.rs

@@ -0,0 +1,84 @@
+use std::collections::BTreeMap;
+use std::str::FromStr;
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{ToPrimitive};
+use serde_json::Value::Null;
+use tracing::{warn};
+use exchanges::bitget_swap_rest::BitgetSwapRest;
+use standard::utils;
+use crate::ExportConnector;
+
+pub struct BitgetSwapExport {
+    request: BitgetSwapRest,
+}
+
+impl BitgetSwapExport {
+    pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> BitgetSwapExport {
+        BitgetSwapExport {
+            request: BitgetSwapRest::new(is_colo, params.clone())
+        }
+    }
+}
+
+#[async_trait]
+impl ExportConnector for BitgetSwapExport {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, _end_time: i64, limit: i64) -> String {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let _limit_params = if limit > 100 {
+            warn!("查询条数最大为100条,已修改为100条!");
+            100
+        } else { limit };
+        let mut last_id = "".to_string();
+        let mut data_array = vec![];
+        loop {
+            let res_data = self.request.get_histories(symbol_format.clone(), 100, last_id.clone()).await;
+            if res_data.code == 200 {
+                if res_data.data["endId"] == Null { break; }
+                last_id = res_data.data["endId"].as_str().unwrap().to_string();
+                let trades_info = res_data.data["fillList"].as_array().unwrap();
+                for value in trades_info.iter() {
+                    let id = value["tradeId"].as_str().unwrap();
+                    let order_id = value["orderId"].as_str().unwrap();
+                    let symbol = value["symbol"].as_str().unwrap();
+                    let side = value["side"].as_str().unwrap();
+                    let price = value["price"].as_str().unwrap();
+                    let size = Decimal::from_str(value["baseVolume"].as_str().unwrap()).unwrap();
+                    let created_time = Decimal::from_str(value["cTime"].as_str().unwrap()).unwrap();
+                    let trade_value = Decimal::from_str(price).unwrap() * size.abs();
+                    let fee = value["feeDetail"][0]["totalFee"].as_str().unwrap();
+                    let profit = value["profit"].as_str().unwrap();
+                    let role = value["tradeScope"].as_str().unwrap();
+                    let created_at = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(created_time.to_i64().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+
+                    data_array.push(vec![
+                        id.to_string(),
+                        order_id.to_string(),
+                        symbol.to_string(),
+                        side.to_string(),
+                        price.to_string(),
+                        size.abs().to_string(),
+                        trade_value.to_string(),
+                        fee.to_string(),
+                        profit.to_string(),
+                        role.to_string(),
+                        created_at,
+                    ]);
+                }
+
+                let last_time = (trades_info.last().unwrap()["cTime"].as_str().unwrap().parse::<i64>().unwrap() * 1000).to_i64().unwrap();
+                if last_time < start_time {
+                    data_array = data_array.iter().filter(|item| {
+                        NaiveDateTime::parse_from_str(item.last().unwrap(), "%Y-%m-%d %H:%M:%S%.3f")
+                            .unwrap()
+                            .timestamp_millis() > start_time
+                    }).cloned().collect();
+                    break;
+                }
+            }
+        }
+        let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "成交价值", "交易费用", "利润", "成交角色", "成交时间"];
+        global::export_utils::export_excel(header_array, data_array, prefix_name)
+    }
+}

+ 201 - 0
derive/src/bybit_swap_export.rs

@@ -0,0 +1,201 @@
+use std::collections::{BTreeMap};
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+use rust_decimal::prelude::ToPrimitive;
+use serde::{Deserialize, Serialize};
+use tracing::{warn};
+use exchanges::bybit_swap_rest::BybitSwapRest;
+use standard::utils;
+use crate::ExportConnector;
+
+pub struct BybitSwapExport {
+    request: BybitSwapRest,
+}
+
+impl BybitSwapExport {
+    pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> BybitSwapExport {
+        BybitSwapExport {
+            request: BybitSwapRest::new(is_colo, params.clone())
+        }
+    }
+}
+
+#[async_trait]
+impl ExportConnector for BybitSwapExport {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+        let pnl_array = get_position_pnl(self.request.clone(), symbol.clone(), start_time.clone(), end_time.clone(), limit.clone()).await;
+        let trades_array = get_trades(self.request.clone(), symbol.clone(), start_time.clone(), end_time.clone(), limit.clone()).await;
+        let pnl_header_array = vec!["订单编号", "交易币对", "买卖方向", "入场价格", "出场价格", "交易类型", "成交数量", "交易盈亏", "创建时间", "更新时间"];
+        let trades_header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "交易类型", "成交价值", "交易费用", "交易费率", "交易时间"];
+        global::export_utils::export_excel_sheets(vec![pnl_header_array, trades_header_array], vec![pnl_array, trades_array], vec!["pnl", "trades"], prefix_name)
+    }
+}
+
+/// TradesSwap
+/// - `symbol`: String,
+/// - `order_id`: String,
+/// - `order_link_id`: String,
+/// - `side`: String,
+/// - `order_price`: String,
+/// - `order_qty`: String,
+/// - `leaves_qty`: String,
+/// - `create_type`: String,
+/// - `order_type`: String,
+/// - `stop_order_type`: String,
+/// - `exec_fee`: String,
+/// - `exec_id`: String,
+/// - `exec_price`: String,
+/// - `exec_qty`: String,
+/// - `exec_type`: String,
+/// - `exec_value`: String,
+/// - `exec_time`: String,
+/// - `fee_currency`: String,
+/// - `is_maker`: bool,
+/// - `fee_rate`: String,
+/// - `trade_iv`: String,
+/// - `mark_iv`: String,
+/// - `mark_price`: String,
+/// - `index_price`: String,
+/// - `underlying_price`: String,
+/// - `block_trade_id`: String,
+/// - `closed_size`: String,
+/// - `seq`: i64,
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct TradesSwap {
+    symbol: String,
+    order_id: String,
+    order_link_id: String,
+    side: String,
+    order_price: String,
+    order_qty: String,
+    leaves_qty: String,
+    create_type: String,
+    order_type: String,
+    stop_order_type: String,
+    exec_fee: String,
+    exec_id: String,
+    exec_price: String,
+    exec_qty: String,
+    exec_type: String,
+    exec_value: String,
+    exec_time: String,
+    fee_currency: String,
+    is_maker: bool,
+    fee_rate: String,
+    trade_iv: String,
+    mark_iv: String,
+    mark_price: String,
+    index_price: String,
+    underlying_price: String,
+    block_trade_id: String,
+    closed_size: String,
+    seq: i64,
+}
+
+async fn get_trades(mut request: BybitSwapRest, symbol: String, start_time: i64, end_time: i64, limit: i64) -> Vec<Vec<String>> {
+    let symbol_format = utils::format_symbol(symbol.clone(), "");
+    let limit_params = if limit > 100 {
+        warn!("查询条数最大为1000条,已修改为100条!");
+        100
+    } else { limit };
+    let mut cursor = "".to_string();
+    let mut data_array = vec![];
+
+    loop {
+        let res_data = request.get_user_trades(symbol_format.clone(), start_time, end_time, limit_params, cursor.clone()).await;
+        if res_data.code == 200 {
+            let data_clone = res_data.data.clone();
+            let trades_info: Vec<TradesSwap> = serde_json::from_str(&*data_clone["list"].to_string()).unwrap();
+            cursor = data_clone["nextPageCursor"].as_str().unwrap_or("").to_string();
+            for value in trades_info.iter() {
+                let time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.exec_time.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+                let order_type = value.order_type.clone();
+
+                data_array.push(vec![
+                    value.exec_id.clone(),
+                    value.order_id.clone(),
+                    value.symbol.clone(),
+                    value.side.clone(),
+                    value.exec_price.clone(),
+                    value.exec_qty.clone(),
+                    order_type.to_string(),
+                    value.exec_value.clone(),
+                    value.exec_fee.clone(),
+                    value.fee_rate.clone(),
+                    time,
+                ]);
+            }
+
+            if trades_info.len().to_i64().unwrap() < limit_params {
+                break;
+            }
+        }
+    }
+    data_array.reverse();
+    data_array
+}
+
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct PositionPnlSwap {
+    symbol: String,
+    order_id: String,
+    side: String,
+    qty: String,
+    order_price: String,
+    order_type: String,
+    exec_type: String,
+    closed_size: String,
+    cum_entry_value: String,
+    avg_entry_price: String,
+    cum_exit_value: String,
+    avg_exit_price: String,
+    closed_pnl: String,
+    fill_count: String,
+    leverage: String,
+    created_time: String,
+    updated_time: String,
+}
+async fn get_position_pnl(mut request: BybitSwapRest, symbol: String, start_time: i64, end_time: i64, limit: i64) -> Vec<Vec<String>> {
+    let symbol_format = utils::format_symbol(symbol.clone(), "");
+    let limit_params = if limit > 100 {
+        warn!("查询条数最大为1000条,已修改为100条!");
+        100
+    } else { limit };
+    let mut cursor = "".to_string();
+    let mut data_array = vec![];
+
+    loop {
+        let res_data = request.get_position_closed_pnl(symbol_format.clone(), start_time, end_time, limit_params, cursor.clone()).await;
+        if res_data.code == 200 {
+            let data_clone = res_data.data.clone();
+            let trades_info: Vec<PositionPnlSwap> = serde_json::from_str(&*data_clone["list"].to_string()).unwrap();
+            cursor = data_clone["nextPageCursor"].as_str().unwrap_or("").to_string();
+            for value in trades_info.iter() {
+                let create_time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.created_time.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+                let update_time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.updated_time.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+
+                data_array.push(vec![
+                    value.order_id.clone(),
+                    value.symbol.clone(),
+                    value.side.clone(),
+                    value.avg_entry_price.clone(),
+                    value.avg_exit_price.clone(),
+                    value.order_type.clone(),
+                    value.closed_size.clone(),
+                    value.closed_pnl.clone(),
+                    create_time,
+                    update_time,
+                ]);
+            }
+
+            if trades_info.len().to_i64().unwrap() < limit_params {
+                break;
+            }
+        }
+    }
+
+    data_array.reverse();
+    data_array
+}

+ 56 - 0
derive/src/export_excel.rs

@@ -0,0 +1,56 @@
+use std::collections::BTreeMap;
+use crate::binance_swap_export::BinanceSwapExport;
+use crate::bybit_swap_export::BybitSwapExport;
+use crate::bitget_swap_export::BitgetSwapExport;
+// use crate::bitget_spot_export::BitgetSpotExport;
+use crate::ExportConnector;
+use crate::gate_swap_export::GateSwapExport;
+// use crate::kucoin_spot_export::KucoinSpotExport;
+// use crate::kucoin_swap_export::KucoinSwapExport;
+// use crate::okx_swap_export::OkxSwapExport;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ExportEnum {
+    BinanceSwap,
+    // KucoinSwap,
+    // KucoinSpot,
+    GateSwap,
+    BybitSwap,
+    BitgetSwap,
+    // BitgetSpot,
+    // OkxSwap,
+}
+
+#[derive(Debug, Clone)]
+pub struct ExportExcel;
+
+impl ExportExcel {
+    pub async fn new(export_enum: ExportEnum, is_colo: bool, params: BTreeMap<String, String>) -> Box<dyn ExportConnector> {
+        match export_enum {
+            ExportEnum::BinanceSwap => {
+                Box::new(BinanceSwapExport::new(is_colo, params).await)
+            }
+            // ExportEnum::KucoinSwap => {
+            //     Box::new(KucoinSwapExport::new(is_colo, params).await)
+            // }
+            // ExportEnum::KucoinSpot => {
+            //     Box::new(KucoinSpotExport::new(is_colo, params).await)
+            // }
+            ExportEnum::GateSwap => {
+                Box::new(GateSwapExport::new(is_colo, params).await)
+            }
+            ExportEnum::BybitSwap => {
+                Box::new(BybitSwapExport::new(is_colo, params).await)
+            }
+            ExportEnum::BitgetSwap => {
+                Box::new(BitgetSwapExport::new(is_colo, params).await)
+            }
+            // ExportEnum::BitgetSpot => {
+            //     Box::new(BitgetSpotExport::new(is_colo, params).await)
+            // }
+            // ExportEnum::OkxSwap => {
+            //     Box::new(OkxSwapExport::new(is_colo, params).await)
+            // }
+        }
+    }
+}

+ 100 - 0
derive/src/gate_swap_export.rs

@@ -0,0 +1,100 @@
+use std::collections::BTreeMap;
+use std::str::FromStr;
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use rust_decimal_macros::dec;
+use serde::{Deserialize, Serialize};
+use tracing::{warn};
+use exchanges::gate_swap_rest::GateSwapRest;
+use standard::utils;
+use crate::ExportConnector;
+
+pub struct GateSwapExport {
+    request: GateSwapRest,
+}
+
+impl GateSwapExport {
+    pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> GateSwapExport {
+        GateSwapExport {
+            request: GateSwapRest::new(is_colo, params.clone())
+        }
+    }
+}
+
+/// TradesSwap
+/// - `id`: i64, 成交记录 ID
+/// - `create_time`: i64, 成交时间
+/// - `contract`: String, 合约标识
+/// - `order_id`: String, 成交记录关联订单 ID
+/// - `size`: i64, 成交数量
+/// - `price`: String, 成交价格
+/// - `text`: String, 成交角色, taker - 吃单, maker - 做单
+/// - `fee`: String, 订单的自定义信息
+/// - `point_fee`: String, 成交手续费
+/// - `role`: String, 成交点卡手续费
+#[derive(Debug, Deserialize, Serialize)]
+struct TradesSwap {
+    id: i64,
+    create_time: f64,
+    contract: String,
+    order_id: String,
+    size: i64,
+    price: String,
+    text: String,
+    fee: String,
+    point_fee: String,
+    role: String,
+}
+
+#[async_trait]
+impl ExportConnector for GateSwapExport {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, _end_time: i64, limit: i64) -> String {
+        let _symbol_array: Vec<&str> = symbol.split("_").collect();
+        let symbol_format = utils::format_symbol(symbol.clone(), "_");
+        let _limit_params = if limit > 1000 {
+            warn!("查询条数最大为1000条,已修改为1000条!");
+            1000
+        } else { limit };
+        let mut last_id = "".to_string();
+        let mut data_array = vec![];
+        loop {
+            let res_data = self.request.my_trades("usdt".to_string(), symbol_format.clone(), 1000, last_id.clone()).await;
+            if res_data.code == 200 {
+                let trades_info: Vec<TradesSwap> = serde_json::from_str(&res_data.data.to_string()).unwrap();
+                for value in trades_info.iter() {
+                    last_id = value.id.to_string();
+                    let size = Decimal::from_i64(value.size).unwrap();
+                    let side = if size < Decimal::ZERO { "sell" } else { "buy" };
+                    let created_time = Decimal::from_f64(value.create_time).unwrap() * dec!(1000);
+                    let created_at = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(created_time.to_i64().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+                    let trade_value = Decimal::from_str(&value.price).unwrap() * size.abs();
+                    data_array.push(vec![
+                        value.id.to_string(),
+                        value.order_id.clone(),
+                        value.contract.clone(),
+                        side.to_string(),
+                        value.price.clone(),
+                        size.abs().to_string(),
+                        trade_value.to_string(),
+                        value.fee.clone(),
+                        value.role.clone(),
+                        created_at,
+                    ]);
+                }
+                let last_time = (trades_info.last().unwrap().create_time * 1000.0).to_i64().unwrap();
+                if last_time < start_time {
+                    data_array = data_array.iter().filter(|item| {
+                        NaiveDateTime::parse_from_str(item.last().unwrap(), "%Y-%m-%d %H:%M:%S%.3f")
+                            .unwrap()
+                            .timestamp_millis() > start_time
+                    }).cloned().collect();
+                    break;
+                }
+            }
+        }
+        let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "成交价值", "交易费用", "成交角色", "成交时间"];
+        global::export_utils::export_excel(header_array, data_array, prefix_name)
+    }
+}

+ 104 - 0
derive/src/kucoin_spot_export.rs

@@ -0,0 +1,104 @@
+// use std::collections::BTreeMap;
+// use async_trait::async_trait;
+// use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+// use serde::{Deserialize, Serialize};
+// use tracing::warn;
+// use exchanges::kucoin_spot_rest::KucoinSpotRest;
+// use standard::exchange::ExchangeEnum;
+// use standard::utils;
+// use crate::ExportConnector;
+//
+// pub struct KucoinSpotExport {
+//     request: KucoinSpotRest,
+// }
+//
+// impl KucoinSpotExport {
+//     pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> KucoinSpotExport {
+//         KucoinSpotExport {
+//             request: KucoinSpotRest::new(is_colo, params.clone())
+//         }
+//     }
+// }
+//
+// /// TradesSwap
+// /// - `symbol`: String, 合約編號
+// /// - `trade_id`: String, 交易編號
+// /// - `order_id`: String, 訂單編號
+// /// - `counter_order_id`: String, 對手方訂單Id
+// /// - `side`: String, 買賣方向
+// /// - `liquidity`: String,流動性類型 taker or maker
+// /// - `force_taker`: bool, 是否強製作爲taker處理
+// /// - `price`: String, 成交價格
+// /// - `size`: i64, 成交數量
+// /// - `funds`: String, 成交額
+// /// - `fee`: String, 交易費用
+// /// - `fee_currency`: String, 收費幣種
+// /// - `stop`: String, 止損單類型標記
+// /// - `fee_rate`: String, 費率
+// /// - `order_type`: String, 訂單類型
+// /// - `created_at`: i64, 創建時間
+// /// - `trade_type`: String, 交易類型: trade, liquidation, ADL or settlement
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct TradesSpot {
+//     symbol: String,
+//     trade_id: String,
+//     order_id: String,
+//     counter_order_id: String,
+//     side: String,
+//     liquidity: String,
+//     force_taker: bool,
+//     price: String,
+//     size: i64,
+//     funds: String,
+//     fee: String,
+//     fee_rate: String,
+//     fee_currency: String,
+//     stop: String,
+//     #[serde(rename = "type")]
+//     order_type: String,
+//     created_at: i64,
+//     trade_type: String,
+// }
+//
+// #[async_trait]
+// impl ExportConnector for KucoinSpotExport {
+//     async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+//         let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
+//         let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+//         let limit_params = if limit > 1000 {
+//             warn!("查询条数最大为1000条,已修改为1000条!");
+//             1000
+//         } else { limit };
+//         let res_data = self.request.get_fills(symbol_format, "".to_string(), "".to_string(), start_time, end_time, limit_params).await;
+//         if res_data.code == "200" {
+//             let res_data_json: serde_json::Value = serde_json::from_str(&res_data.data).unwrap();
+//             let trades_info: Vec<TradesSpot> = serde_json::from_str(&res_data_json["items"].to_string()).unwrap();
+//             let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "订单价格", "订单数量", "成交额", "手续费", "手续费率", "订单类型", "创建时间"];
+//             let mut data_array: Vec<Vec<String>> = Vec::new();
+//             for (index, value) in trades_info.iter().enumerate() {
+//                 if index >= data_array.len() {
+//                     data_array.push(Vec::new());
+//                 }
+//                 let created_at = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.created_at.clone()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+//
+//                 data_array[index] = vec![
+//                     value.trade_id.clone(),
+//                     value.order_id.clone(),
+//                     value.symbol.clone(),
+//                     value.side.clone(),
+//                     value.price.clone(),
+//                     value.size.to_string(),
+//                     value.funds.clone(),
+//                     value.fee.clone(),
+//                     value.fee_rate.clone(),
+//                     value.order_type.clone(),
+//                     created_at,
+//                 ];
+//             }
+//             global::export_utils::export_excel(header_array, data_array, prefix_name)
+//         } else {
+//             res_data.to_string()
+//         }
+//     }
+// }

+ 110 - 0
derive/src/kucoin_swap_export.rs

@@ -0,0 +1,110 @@
+use std::collections::BTreeMap;
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone, Utc};
+use serde::{Deserialize, Serialize};
+use tracing::warn;
+use exchanges::kucoin_swap_rest::KucoinSwapRest;
+use standard::exchange::ExchangeEnum;
+use standard::utils;
+use crate::ExportConnector;
+
+pub struct KucoinSwapExport {
+    request: KucoinSwapRest,
+}
+
+impl KucoinSwapExport {
+    pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> KucoinSwapExport {
+        KucoinSwapExport {
+            request: KucoinSwapRest::new(is_colo, params.clone())
+        }
+    }
+}
+
+/// TradesSwap
+/// - `symbol`: String, 合約編號
+/// - `trade_id`: String, 交易編號
+/// - `order_id`: String, 訂單編號
+/// - `side`: String, 買賣方向
+/// - `liquidity`: String,流動性類型 taker or maker
+/// - `force_taker`: bool, 是否強製作爲taker處理
+/// - `price`: String, 成交價格
+/// - `size`: i64, 成交數量
+/// - `value`: String, 成交價值
+/// - `fee_rate`: String, 費率
+/// - `fix_fee`: String, 固定費用
+/// - `fee_currency`: String, 收費幣種
+/// - `stop`: String, 止損單類型標記
+/// - `fee`: String, 交易費用
+/// - `order_type`: String, 訂單類型
+/// - `trade_type`: String, 交易類型: trade, liquidation, ADL or settlement
+/// - `created_at`: i64, 創建時間
+/// - `settle_currency`: String, 結算幣種
+/// - `trade_time`: i64, 交易時間納秒
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct TradesSwap {
+    symbol: String,
+    trade_id: String,
+    order_id: String,
+    side: String,
+    liquidity: String,
+    force_taker: bool,
+    price: String,
+    size: i64,
+    value: String,
+    fee_rate: String,
+    fix_fee: String,
+    fee_currency: String,
+    stop: String,
+    fee: String,
+    order_type: String,
+    trade_type: String,
+    created_at: i64,
+    settle_currency: String,
+    trade_time: i64,
+}
+
+#[async_trait]
+impl ExportConnector for KucoinSwapExport {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+        let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
+        let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
+        let limit_params = if limit > 1000 {
+            warn!("查询条数最大为1000条,已修改为1000条!");
+            1000
+        } else { limit };
+        let res_data = self.request.get_fills(symbol_format, "".to_string(), "".to_string(), start_time, end_time, limit_params).await;
+        if res_data.code == "200" {
+            let res_data_json: serde_json::Value = serde_json::from_str(&res_data.data).unwrap();
+            let trades_info: Vec<TradesSwap> = serde_json::from_str(&res_data_json["items"].to_string()).unwrap();
+            let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "成交价值", "费率", "固定费用", "交易费用", "订单类型", "创建时间", "交易时间"];
+            let mut data_array: Vec<Vec<String>> = Vec::new();
+            for (index, value) in trades_info.iter().enumerate() {
+                if index >= data_array.len() {
+                    data_array.push(Vec::new());
+                }
+                let created_at = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.created_at.clone()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+                let trade_time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&Utc.timestamp_nanos(value.trade_time.clone()).naive_utc()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+
+                data_array[index] = vec![
+                    value.trade_id.clone(),
+                    value.order_id.clone(),
+                    value.symbol.clone(),
+                    value.side.clone(),
+                    value.price.clone(),
+                    value.size.to_string(),
+                    value.value.clone(),
+                    value.fee_rate.clone(),
+                    value.fix_fee.clone(),
+                    value.fee.clone(),
+                    value.order_type.clone(),
+                    created_at,
+                    trade_time,
+                ];
+            }
+            global::export_utils::export_excel(header_array, data_array, prefix_name)
+        } else {
+            res_data.to_string()
+        }
+    }
+}

+ 16 - 0
derive/src/lib.rs

@@ -0,0 +1,16 @@
+use async_trait::async_trait;
+
+mod binance_swap_export;
+pub mod export_excel;
+// mod kucoin_swap_export;
+// mod kucoin_spot_export;
+mod gate_swap_export;
+mod bitget_spot_export;
+mod okx_swap_export;
+mod bybit_swap_export;
+mod bitget_swap_export;
+
+#[async_trait]
+pub trait ExportConnector {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String;
+}

+ 120 - 0
derive/src/okx_swap_export.rs

@@ -0,0 +1,120 @@
+// use std::collections::BTreeMap;
+// use std::str::FromStr;
+// use async_trait::async_trait;
+// use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+// use rust_decimal::Decimal;
+// use serde::{Deserialize, Serialize};
+// use tracing::warn;
+// use exchanges::okx_swap_rest::OkxSwapRest;
+// use standard::utils;
+// use crate::ExportConnector;
+//
+// pub struct OkxSwapExport {
+//     request: OkxSwapRest,
+// }
+//
+// impl OkxSwapExport {
+//     pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> OkxSwapExport {
+//         OkxSwapExport {
+//             request: OkxSwapRest::new(is_colo, params.clone())
+//         }
+//     }
+// }
+//
+// /// TradesSwap
+// ///
+// /// - `inst_type`: String,
+// /// - `inst_id`: String,
+// /// - `trade_id`: String,
+// /// - `ord_id`: String,
+// /// - `cl_ord_id`: String,
+// /// - `bill_id`: String,
+// /// - `tag`: String,
+// /// - `fill_px`: String,
+// /// - `fill_sz`: String,
+// /// - `fill_idx_px`: String,
+// /// - `fill_pnl`: String,
+// /// - `fill_px_vol`: String,
+// /// - `fill_px_usd`: String,
+// /// - `fill_mark_vol`: String,
+// /// - `fill_fwd_px`: String,
+// /// - `fill_mark_px`: String,
+// /// - `side`: String,
+// /// - `pos_side`: String,
+// /// - `exec_type`: String,
+// /// - `fee_ccy`: String,
+// /// - `fee`: String,
+// /// - `ts`: String,
+// /// - `fill_time`: String,
+// #[derive(Debug, Deserialize, Serialize)]
+// #[serde(rename_all = "camelCase")]
+// struct TradesSwap {
+//     inst_type: String,
+//     inst_id: String,
+//     trade_id: String,
+//     ord_id: String,
+//     cl_ord_id: String,
+//     bill_id: String,
+//     tag: String,
+//     fill_px: String,
+//     fill_sz: String,
+//     fill_idx_px: String,
+//     fill_pnl: String,
+//     fill_px_vol: String,
+//     fill_px_usd: String,
+//     fill_mark_vol: String,
+//     fill_fwd_px: String,
+//     fill_mark_px: String,
+//     side: String,
+//     pos_side: String,
+//     exec_type: String,
+//     fee_ccy: String,
+//     fee: String,
+//     ts: String,
+//     fill_time: String,
+// }
+//
+// #[async_trait]
+// impl ExportConnector for OkxSwapExport {
+//     async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+//         let symbol_format = utils::format_symbol(symbol.clone(), "-");
+//         let limit_params = if limit > 100 {
+//             warn!("查询条数最大为100条,已修改为100条!");
+//             1000
+//         } else { limit };
+//         let start_time_str = if start_time > 0 { start_time.to_string() } else { "".to_string() };
+//         let end_time_str = if end_time > 0 { end_time.to_string() } else { "".to_string() };
+//         let limit_str = if limit_params > 0 { limit_params.to_string() } else { "".to_string() };
+//         let res_data = self.request.get_trade_fills_history(symbol_format, start_time_str, end_time_str, limit_str).await;
+//         if res_data.code == "200" {
+//             let trades_info: Vec<TradesSwap> = serde_json::from_str(&res_data.data).unwrap();
+//             let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "成交价值", "交易费用", "创建时间", "成交时间"];
+//             let mut data_array: Vec<Vec<String>> = Vec::new();
+//
+//             for (index, value) in trades_info.iter().enumerate() {
+//                 if index >= data_array.len() {
+//                     data_array.push(Vec::new());
+//                 }
+//
+//                 let created_at = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.ts.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+//                 let trade_time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.fill_time.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+//                 let trade_value = Decimal::from_str(&value.fill_px).unwrap() * Decimal::from_str(&value.fill_sz).unwrap();
+//                 data_array[index] = vec![
+//                     value.trade_id.clone(),
+//                     value.ord_id.clone(),
+//                     value.inst_id.clone(),
+//                     value.side.clone(),
+//                     value.fill_px.clone(),
+//                     value.fill_sz.clone(),
+//                     trade_value.to_string(),
+//                     value.fee.clone(),
+//                     created_at,
+//                     trade_time,
+//                 ];
+//             }
+//             global::export_utils::export_excel(header_array, data_array, prefix_name)
+//         } else {
+//             res_data.to_string()
+//         }
+//     }
+// }

+ 2 - 0
exchanges/.gitignore

@@ -0,0 +1,2 @@
+/target
+/.idea

+ 52 - 0
exchanges/Cargo.toml

@@ -0,0 +1,52 @@
+[package]
+name = "exchanges"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+# json
+serde_json = "1.0.104"
+#tungstenite = { git = "https://github.com/PrivateRookie/tungstenite-rs.git", rev = "1d9289276518e5ab7e5194126d40b441d8938375" }
+#tungstenite = { git = "https://github.com/PrivateRookie/tungstenite-rs.git", rev = "f368f3087d50d97658fda5337550e587bb1ba1b6" }
+
+tokio-tungstenite= { git = "https://github.com/HonestHouLiang/tokio-tungstenite.git",rev = "208fc9b09bcc2e2c8cb52e1cde5087446464fc91"  }
+futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
+futures-channel = "0.3.28"
+
+url = "2.4.0"
+base64 = "0.13"
+tokio = { version = "1.31.0", features = ["full"] }
+chrono = "0.4.26"
+hex = "0.4"
+reqwest = { version = "0.11.22", features = ["json","socks"] }
+# 解壓縮數據
+flate2 = "1.0"
+
+
+ring = "0.16.20"
+data-encoding = "2.4.0"
+
+
+hmac = "0.8.1"
+sha2 = "0.9.8"
+#tokio-tungstenite = "0.14"
+
+##代替f64避免精度丢失
+rust_decimal = "1.32.0"
+rust_decimal_macros = "1.32.0"
+
+
+##日志
+global = { path="../global" }
+tracing = "0.1"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+
+##生成 xlsx
+rust_xlsxwriter = "0.58.0"
+
+once_cell = "1.18.0"
+
+##url编码
+percent-encoding = "2.1.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
+//     }
+// }

+ 533 - 0
exchanges/src/binance_swap_rest.rs

@@ -0,0 +1,533 @@
+use std::collections::BTreeMap;
+
+use hex;
+use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
+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::{json, Value};
+use tracing::{error, info, trace};
+
+use crate::http_tool::RestTool;
+use crate::proxy;
+use crate::response_base::ResponseData;
+
+#[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"),
+                                false,
+                                params,
+        ).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"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //账户信息
+    pub async fn get_account(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v2/balance"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //持仓模式
+    pub async fn get_pos_side(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/positionSide/dual"),
+                                true,
+                                params,
+        ).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
+         });
+        let data = self.request("POST".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/positionSide/dual"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //调整开仓杠杆
+    pub async fn setting_dual_leverage(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/leverage"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //查询订单
+    pub async fn get_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/order"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //查询当前挂单
+    pub async fn get_open_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/openOrder"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //查看当前全部挂单
+    pub async fn get_open_orders(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/openOrders"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //查询所有订单(包括历史订单)
+    pub async fn get_all_orders(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/allOrders"),
+                                true,
+                                params,
+        ).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,
+        ).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,
+        ).await;
+        data
+    }
+    //用户持仓风险V2
+    pub async fn get_position_risk(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol
+         });
+
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v2/positionRisk"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //撤销订单
+    pub async fn cancel_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/order"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //根据币对 撤销全部订单
+    pub async fn cancel_order_all(&mut self, params: Value) -> ResponseData {
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/allOpenOrders"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //批量撤销订单
+    pub async fn cancel_order_batch(&mut self, params: Value) -> ResponseData {
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/batchOrders"),
+                                true,
+                                params,
+        ).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
+         });
+        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,
+        ).await;
+        data
+    }
+
+    //生成listenKey (USER_STREAM)
+    pub async fn get_listen_key(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/listenKey"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //延长listenKey有效期 (USER_STREAM)
+    pub async fn up_listen_key(&mut self, params: Value) -> ResponseData {
+        let data = self.request("PUT".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/listenKey"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+    //关闭listenKey (USER_STREAM) (USER_STREAM)
+    pub async fn close_listen_key(&mut self, params: Value) -> ResponseData {
+        let data = self.request("PUT".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/listenKey"),
+                                true,
+                                params,
+        ).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,
+                         mut params_json: Value) -> 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 timestamp = chrono::Utc::now().timestamp_millis();
+        params_json["timestamp"] = Value::from(timestamp);
+        params_json["recvWindow"] = Value::from(2000);
+
+
+        let mut headers = HeaderMap::new();
+        if method == "GET" {
+            headers.insert("Content-Type", "application/json; charset=UTF-8".parse().unwrap());
+        } else if method == "POST" || method == "PUT" || method == "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 {
+                //需要登录-且登录参数齐全
+                trace!("this------params_json:{}", params_json.clone());
+                //组装sing
+                let sing = Self::sign(secret_key.clone(), params_json.to_string());
+                params_json["signature"] = serde_json::Value::from(sing.clone());
+                // trace!("sing:{}", sing);
+                //组装header
+                headers.extend(Self::headers(access_key));
+            }
+        }
+
+        let z = params_json.to_string();
+        let body = RestTool::parse_params_to_str(z.clone());
+        let params = RestTool::parse_params_to_str(z.clone());
+
+
+        trace!("headers:{:?}", headers);
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.clone(),
+            body.clone(),
+            headers,
+            is_login,
+        ).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);
+        // trace!("this------message:{}", message.clone());
+
+
+        // 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);
+        // // '\n'
+        // let sign_1 = sign.replace("\n", "");
+        // // '=' '/'
+        // let sign_2 = utf8_percent_encode(sign_1.as_str(), NON_ALPHANUMERIC).to_string();
+        //
+        // sign_2
+
+        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());
+        let sign_1 = sign.replace("\n", "");
+        let sign_2 = utf8_percent_encode(sign_1.as_str(), NON_ALPHANUMERIC).to_string();
+
+        sign_2
+    }
+
+
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: String,
+                       headers: HeaderMap,
+                       is_login: bool) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let addrs_url: String = if params == "" {
+            url.clone()
+        } else {
+            format!("{}?{}", url.clone(), params)
+        };
+
+        trace!("url-----:{}",url.clone());
+        trace!("addrs_url-----:{}",addrs_url.clone());
+        trace!("params-----:{}",params.clone());
+        trace!("body-----:{}",body.clone());
+        trace!("headers-----:{:?}",headers.clone());
+        trace!("request_type-----:{:?}",request_type.clone());
+
+
+        if is_login {
+            proxy::ParsingDetail::http_enable_proxy(Some("binance"));
+        } else {
+            proxy::ParsingDetail::http_enable_proxy(None);
+        };
+
+        // let client = Client::new();
+
+        let request_builder = match request_type.as_str() {
+            "GET" => self.client.get(addrs_url.clone()).headers(headers),
+            "POST" => self.client.post(url.clone()).body(body).headers(headers),
+            "DELETE" => self.client.delete(addrs_url.clone()).headers(headers),
+            "PUT" => self.client.put(addrs_url.clone()).headers(headers),
+            _ => {
+                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();
+        // trace!("text:???{:?}",text);
+        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 = data["code"].as_i64().unwrap() as i16;
+                error.message = format!("请求地址:{}, 请求参数:{}", base_url, params);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(), format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 466 - 0
exchanges/src/binance_swap_ws.rs

@@ -0,0 +1,466 @@
+use std::borrow::Cow;
+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::{json, Value};
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tokio_tungstenite::tungstenite::protocol::CloseFrame;
+use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode;
+use tracing::{error, info, trace};
+use crate::binance_swap_rest::BinanceSwapRest;
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode};
+
+//类型
+pub enum BinanceSwapWsType {
+    Public,
+    Private,
+}
+
+//订阅频道
+#[derive(Clone)]
+pub enum BinanceSwapSubscribeType {
+    PuBookTicker,
+    PuAggTrade,
+    PuDepth20levels100ms,
+
+    PrAccount,
+    PrBalance,
+    PrPosition,
+}
+
+//账号信息
+#[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 async 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).await;
+    }
+    pub async fn new_label(label: String, is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            BinanceSwapWsType::Public => {
+                // "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms".to_string(),
+                "wss://fstream.binance.com/stream".to_string()
+            }
+            BinanceSwapWsType::Private => {
+                "wss://fstream.binance.com/ws/".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,
+
+                BinanceSwapSubscribeType::PrAccount => { true }
+                BinanceSwapSubscribeType::PrBalance => { true }
+                BinanceSwapSubscribeType::PrPosition => { true }
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string_pu(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)
+            }
+
+            BinanceSwapSubscribeType::PrAccount => {
+                "".to_string()
+            }
+            BinanceSwapSubscribeType::PrBalance => {
+                "".to_string()
+            }
+            BinanceSwapSubscribeType::PrPosition => {
+                "".to_string()
+            }
+        }
+    }
+    pub fn enum_to_string_pr(subscribe_type: BinanceSwapSubscribeType) -> String {
+        match subscribe_type {
+            BinanceSwapSubscribeType::PuAggTrade => {
+                "".to_string()
+            }
+            BinanceSwapSubscribeType::PuDepth20levels100ms => {
+                "".to_string()
+            }
+            BinanceSwapSubscribeType::PuBookTicker => {
+                "".to_string()
+            }
+            BinanceSwapSubscribeType::PrAccount => {
+                "@account".to_string()
+            }
+            BinanceSwapSubscribeType::PrBalance => {
+                "@balance".to_string()
+            }
+            BinanceSwapSubscribeType::PrPosition => {
+                "@position".to_string()
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription_pu(&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_pu(symbol.clone(), subscribe_type.clone());
+                if ty_str != "".to_string() {
+                    params.push(ty_str);
+                }
+            }
+        }
+
+        if params.len() > 0 {
+            let str = json!({
+            "method": "SUBSCRIBE",
+            "params":  params,
+            "id": 1
+            });
+            str.to_string()
+        } else {
+            "".to_string()
+        }
+    }
+    pub fn get_subscription_pr(&self) -> Vec<String> {
+        let mut params = vec![];
+        for subscribe_type in &self.subscribe_types {
+            let ty_str = Self::enum_to_string_pr(subscribe_type.clone());
+            if ty_str != "".to_string() {
+                params.push(ty_str);
+            }
+        }
+
+        params
+
+        // if (params.len() > 0) {
+        //     let str = json!({
+        //     "method": "REQUEST",
+        //     "params":params,
+        //     "id": 12 // request ID.
+        // });
+        //     str.to_string()
+        // } else {
+        //     "".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_pu = self.get_subscription_pu();
+        let subscription_pr = self.get_subscription_pr();
+        let address_url = self.address_url.clone();
+        let label = self.label.clone();
+        // let heartbeat_time: u64 = self.heartbeat_time.clone();
+        let login_param_dto = self.login_param.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!("线程-异步心跳-结束");
+        // });
+        //60分钟效果 50之后分钟重连
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        tokio::spawn(async move {
+            trace!("线程-异步时效记录-开始");
+            // let time_5m = 1000 * 60 * 50;
+            let reconnect_interval = 1000 * 60 * 50;
+            loop {
+                tokio::time::sleep(Duration::from_millis(reconnect_interval)).await;
+                let write_tx_clone = write_tx_clone1.lock().await;
+                let close_frame = CloseFrame {
+                    code: CloseCode::Normal,
+                    reason: Cow::Borrowed("Bye bye"),
+                };
+                let message = Message::Close(Some(close_frame));
+                match write_tx_clone.unbounded_send(message) {
+                    Ok(_o) => {
+                        info!("listen_key 时效不足60,更新listen_key,发起重启指令..");
+                    }
+                    Err(k) => {
+                        error!("listen_key 时效不足60,更新listen_key,发起关闭指令..发起失败:原因{:?}",k)
+                    }
+                }
+            }
+            // AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Custom(), time_5m).await;
+            // trace!("线程-异步时效记录-结束");
+        });
+
+        //设置订阅
+        // let mut subscribe_array = vec![];
+        //
+        // if (subscription_pu.len() > 0) {
+        //     subscribe_array.push(subscription_pu.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 连接中……");
+                // get listen_key
+                let listen_key = if login_is {
+                    match login_param_dto.clone() {
+                        None => {
+                            "".to_string()
+                        }
+                        Some(dto) => {
+                            let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+                            btree_map.insert("access_key".to_string(), dto.api_key.clone());
+                            btree_map.insert("secret_key".to_string(), dto.api_secret.clone());
+
+                            let mut ba_exc = BinanceSwapRest::new(false, btree_map);
+                            let rep_data = ba_exc.get_listen_key(json!({   })).await;
+                            trace!("拿到-rep_data:{:?}",rep_data);
+                            if rep_data.code == 200 {
+                                let listen_key_str = rep_data.data["listenKey"].as_str().unwrap();
+                                // trace!("拿到-data:{}",rep_data.data.clone());
+                                // trace!("拿到-listenKey:{}",listen_key_str.clone());
+                                // format!("{}{}",  address_url.clone(), listenKey
+                                listen_key_str.to_string()
+                            } else {
+                                "".to_string()
+                            }
+                        }
+                    }
+                } else {
+                    "".to_string()
+                };
+
+                let this_address_url = if login_is {
+                    if listen_key.len() == 0 {
+                        error!("binance_usdt_swap socket get listenKey, is error");
+                        break;
+                    }
+
+                    format!("{}{}", address_url.clone(), listen_key)
+                } else {
+                    address_url.clone()
+                };
+
+                let mut subscribe_array = vec![];
+                if login_is {
+                    let this_subscription_pr = subscription_pr.clone();
+                    let mut params_pr: Vec<String> = vec![];
+                    for dto in this_subscription_pr {
+                        params_pr.push(format!("{}{}", listen_key, dto))
+                    }
+                    let json_dto = json!({
+                            "method": "REQUEST",
+                            "params":params_pr,
+                            "id": 12 // request ID.
+                       });
+                    subscribe_array.push(json_dto.to_string());
+                } else {
+                    subscribe_array.push(subscription_pu.to_string());
+                }
+
+                // ws层重连
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), this_address_url.clone(),
+                                                 false, label.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong, Self::message_binary).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 message_binary(_po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = format!("Binary:{:?}", _po);
+        Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData {
+        // trace!("原始数据");
+        // 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("result").is_some() && json_value.get("id").is_some() &&
+            json_value.get("id").unwrap() == 12
+        {
+            //私有订阅
+            let result = json_value["result"].as_array().unwrap();
+            let mut message = "".to_string();
+            for dto in result.clone() {
+                let req = dto["req"].as_str().unwrap();
+                if req.contains("@account") {
+                    message += "@aggTrade";
+                } else if req.contains("@balance") {
+                    message += "@balance";
+                }else if req.contains("@position") {
+                    message += "@position";
+                }
+            }
+
+            res_data.data = Value::from(result.clone());
+            res_data.code = 200;
+            res_data.message = format!("私有订阅反馈:{}", message);
+            res_data.channel = "info".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 if json_value.get("e").is_some() {
+            //推送私有数据
+            res_data.data = json_value.clone();
+            res_data.code = 200;
+
+            let channel = json_value["e"].as_str().unwrap();
+            if channel.contains("ACCOUNT_UPDATE") {
+                res_data.channel = "ACCOUNT_UPDATE".to_string();
+            } else if channel.contains("ORDER_TRADE_UPDATE") {
+                res_data.channel = "ORDER_TRADE_UPDATE".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
+    }
+}

+ 314 - 0
exchanges/src/bingx_swap_rest.rs

@@ -0,0 +1,314 @@
+use std::collections::BTreeMap;
+use chrono::Utc;
+use reqwest::header::HeaderMap;
+use reqwest::{Client};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::{error, info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use serde_json::Value;
+
+#[derive(Clone, Debug)]
+pub struct BingxSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl BingxSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BingxSwapRest
+    {
+        return BingxSwapRest::new_with_tag("default-BingxSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BingxSwapRest {
+        let base_url = if is_colo {
+            "https://open-api.bingx.com".to_string()
+        } else {
+            "https://open-api.bingx.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        BingxSwapRest {
+            tag,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //查詢合約基礎信息
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/openApi/swap/v2".to_string(),
+                                "/quote/contracts".to_string(),
+                                false,
+                                params,
+        ).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,
+                         mut  params: Value) -> 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 timestamp = Utc::now().timestamp_millis();
+        let recv_window = 3000;
+        params["timestamp"] = serde_json::json!(timestamp);
+        params["recvWindow"] = serde_json::json!(recv_window);
+
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/x-www-form-urlencoded".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value);
+
+        let code = json_value["code"].as_i64().unwrap();
+        match code {
+            0 => {
+                //判断是否有code ,没有表示特殊接口,直接返回
+                if json_value.get("data").is_some() {
+                    let data = json_value.get("data").unwrap();
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), data.clone())
+                } else {
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value)
+                }
+            }
+            _ => {
+                ResponseData::new(self.tag.clone(), 400, "error".to_string(), json_value)
+            }
+        }
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 318 - 0
exchanges/src/bingx_swap_ws.rs

@@ -0,0 +1,318 @@
+use std::io::Read;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use flate2::read::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::json;
+use serde_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 BingxSwapWsType {
+    PublicAndPrivate,
+}
+
+
+#[derive(Debug)]
+#[derive(Clone)]
+pub struct BingxSwapWsParam {
+    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 BingxSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+}
+
+//账号信息
+#[derive(Clone, Debug)]
+pub struct BingxSwapLogin {
+    pub access_key: String,
+    pub secret_key: String,
+    pub pass_key: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BingxSwapWs {
+    //类型
+    tag: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<BingxSwapLogin>,
+    //登录数据
+    ws_param: BingxSwapWsParam,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<BingxSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl BingxSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<BingxSwapLogin>, ws_type: BingxSwapWsType) -> BingxSwapWs {
+        return Self::new_with_tag("default-BingxSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: Option<BingxSwapLogin>, ws_type: BingxSwapWsType) -> BingxSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            BingxSwapWsType::PublicAndPrivate => {
+                let url = "wss://open-api-swap.bingx.com/swap-market".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        /*******公共频道-私有频道数据组装*/
+        let ws_param = BingxSwapWsParam {
+            token: "".to_string(),
+            ws_url: "".to_string(),
+            ws_ping_interval: 0,
+            ws_ping_timeout: 0,
+            is_ok_subscribe: false,
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        BingxSwapWs {
+            tag,
+            address_url,
+            login_param,
+            ws_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 18,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BingxSwapSubscribeType>) {
+        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 {
+                BingxSwapSubscribeType::PuFuturesTrades => false,
+                BingxSwapSubscribeType::PuFuturesRecords => false,
+                BingxSwapSubscribeType::PuFuturesDepth => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: BingxSwapSubscribeType) -> String {
+        match subscribe_type {
+            BingxSwapSubscribeType::PuFuturesDepth => {
+                format!("{}@depth5@100ms", symbol)
+            }
+            BingxSwapSubscribeType::PuFuturesRecords => {
+                format!("{}@kline_1m", symbol)
+            }
+            BingxSwapSubscribeType::PuFuturesTrades => {
+                format!("{}@trade", symbol)
+            }
+        }
+    }
+    //订阅信息生成
+    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());
+                let str = json!({
+                    "id": "id1",
+                    "reqType": "sub",
+                    "dataType": ty_str.to_string()
+                });
+                array.push(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 tag = self.tag.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!("Bingx_usdt_swap socket 连接中……");
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, tag.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong, Self::message_binary).await;
+
+                error!("Bingx_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text);
+        Option::from(response_data)
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -300, "success".to_string(), Value::Null));
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null));
+    }
+    //数据解析-二进制
+    pub fn message_binary(po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        // let message_str = format!("Binary:{:?}", _po);
+        // Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+        // let result = String::from_utf8(bytes);
+        // let result = String::from_utf8(po);
+
+        let mut gz_decoder = GzDecoder::new(&po[..]);
+        let mut decompressed_data = Vec::new();
+
+        // 尝试解压数据
+        if let Ok(_) = gz_decoder.read_to_end(&mut decompressed_data) {
+            // 将解压后的字节向量转换为 UTF-8 字符串
+            match String::from_utf8(decompressed_data) {
+                Ok(text) => {
+                    let response_data = Self::ok_text(text);
+                    return Option::from(response_data);
+                }
+                Err(_) => {
+                    return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".to_string(), Value::Null));
+                }
+            }
+        } else {
+            return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".to_string(), Value::Null));
+        }
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData
+    {
+        // trace!("原始数据:{:?}",text);
+        match text.as_str() {
+            "Ping" => {
+                return ResponseData::new("".to_string(), -300, "success".to_string(), Value::String(String::from("Pong")));
+            }
+            _ => {}
+        }
+        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();
+
+        // { "id": "id1", "code": 0, "msg": "" }
+        if json_value["id"].as_str() == Option::from("id1") {
+            //订阅
+            if json_value["code"].as_i64() == Option::from(0) {
+                res_data.code = -201;
+                res_data.message = "订阅成功".to_string();
+            } else {
+                res_data.code = 400;
+                res_data.message = "订阅失败".to_string();
+            }
+        } else if json_value["code"].as_i64() == Option::from(0) {
+            res_data.code = 200;
+            res_data.data = json_value.clone();
+
+            //订阅数据 甄别
+            let data_type = json_value["dataType"].as_str().unwrap();
+            if data_type.contains("@depth") {
+                res_data.channel = "futures.order_book".to_string();
+            } else if data_type.contains("@trade") {
+                res_data.channel = "futures.trades".to_string();
+            } else if data_type.contains("@kline_1m") {
+                res_data.channel = "futures.candlesticks".to_string();
+            } else {
+                res_data.channel = "未知推送数据".to_string();
+            }
+        } else {
+            res_data.code = -1;
+            res_data.message = "未知解析".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
+//         }
+//     }
+// }

+ 420 - 0
exchanges/src/bitget_swap_rest.rs

@@ -0,0 +1,420 @@
+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::{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_histories(&mut self, symbol: String, limit: i64, last_id: String) -> ResponseData {
+        let mut params = json!({
+            "productType": "USDT-FUTURES",
+            "idLessThan": last_id,
+            "limit": limit
+         });
+        if symbol != "" { params["symbol"] = json!(symbol); }
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/order/fill-history".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
+    }
+
+    // 获取K线
+    pub async fn get_market_candles(&mut self, params: Value) -> ResponseData {
+        self.request("GET".to_string(),
+                     "/api/v2".to_string(),
+                     "/mix/market/candles".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
+    }
+}

+ 351 - 0
exchanges/src/bitget_swap_ws.rs

@@ -0,0 +1,351 @@
+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,
+    PuKline(String),
+
+    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::PuKline(_) => 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::PuKline(t) => {
+                json!({
+                    "instType": "USDT-FUTURES",
+                    "channel": format!("candle{}m", t),
+                    "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, Self::message_binary).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 message_binary(_po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = format!("Binary:{:?}", _po);
+        Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData {
+        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() {
+                    let channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+                    res_data.data = json_value.clone();
+                    res_data.reach_time = json_value["ts"].as_i64().unwrap() * 1000;
+                    res_data.message = "success".to_string();
+
+                    res_data.code = 200;
+
+                    if channel.contains("books1") {
+                        res_data.channel = "orderbook".to_string();
+                        res_data.data_type = json_value["action"].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("trade") {
+                        res_data.channel = "trade".to_string();
+                        res_data.data_type = json_value["action"].as_str().unwrap().to_string();
+                    } else if channel.contains("ticker") {
+                        res_data.channel = "tickers".to_string();
+                        res_data.data["ts"] = json_value["ts"].clone();
+                        res_data.data_type = json_value["action"].as_str().unwrap().to_string();
+                    } else if channel.contains("candle") {
+                        res_data.channel = "kline".to_string();
+                        res_data.data_type = json_value["action"].as_str().unwrap().to_string();
+                    } else if channel.contains("positions") {
+                        res_data.channel = "position".to_string();
+                        res_data.data_type = json_value["action"].as_str().unwrap().to_string();
+                    } else if channel.contains("orders") {
+                        res_data.channel = "order".to_string();
+                        res_data.data_type = json_value["action"].as_str().unwrap().to_string();
+                    } else if channel.contains("account") {
+                        res_data.channel = "wallet".to_string();
+                        res_data.data_type = json_value["action"].as_str().unwrap().to_string();
+                    } else {
+                        res_data.code = -1;
+                        res_data.channel = "未知的频道".to_string();
+                    }
+                }
+            }
+        }
+
+        res_data
+    }
+}

+ 399 - 0
exchanges/src/bitmart_swap_rest.rs

@@ -0,0 +1,399 @@
+use std::collections::BTreeMap;
+use chrono::Utc;
+use reqwest::Client;
+use reqwest::header::HeaderMap;
+use ring::hmac;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::{json, Value};
+use tracing::{info, trace};
+use std::borrow::Borrow;
+
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+
+#[derive(Clone)]
+pub struct BitMartSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: Client,
+    login_param: BTreeMap<String, String>,                  // 登录参数
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl BitMartSwapRest {
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BitMartSwapRest {
+        return BitMartSwapRest::new_with_tag("default-BitMartSwapRest".to_string(), is_colo, login_param);
+    }
+    // 构造Bitget,可以自定义tag
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BitMartSwapRest {
+        let base_url = if is_colo {
+            "https://api-cloud.bitmart.com".to_string()
+        } else {
+            "https://api-cloud.bitmart.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道", base_url);
+        } else {
+            info!("走普通通道:{}", base_url);
+        }
+
+        BitMartSwapRest {
+            tag,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: Decimal::ZERO,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+
+    //获取服务器时间
+    pub async fn get_server_time(&mut self) -> ResponseData {
+        let params = json!({
+        });
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/system/time"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+    //深度
+    pub async fn get_depth(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/contract".to_string(),
+                                format!("/public/depth"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+    //获取所有合约详情
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/contract".to_string(),
+                                format!("/public/details"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //查询合约账户 (查询合约资产明细 )
+    pub async fn get_account(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/assets-detail"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //用户仓位列表(查询仓位详情 (KEYED))
+    pub async fn get_user_position(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/position"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //查询合约订单列表 (查询合约历史订单 (KEYED))
+    pub async fn get_orders(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/order-history"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //查询单个订单详情 (【全仓】获取订单明细信息)
+    pub async fn get_order_details(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/order"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //合约交易下单 (合约下单 (SIGNED))
+    pub async fn swap_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/submit-order"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    // 全部撤单(批量撤销合约订单 (SIGNED))
+    pub async fn cancel_price_order(&mut self, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/contract".to_string(),
+                     format!("/private/cancel-orders"),
+                     true,
+                     params,
+        ).await
+    }
+
+    //撤销单个订单 (取消单个合约订单 (SIGNED))
+    pub async fn cancel_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/cancel-order"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    // //设置持仓模式 (【全仓】切换持仓模式)
+    // pub async fn setting_dual_mode(&mut self, params: Value) -> ResponseData {
+    //     let data = self.request("POST".to_string(),
+    //                             "/linear-swap-api/v1".to_string(),
+    //                             format!("/swap_cross_switch_position_mode"),
+    //                             true,
+    //                             params,
+    //     ).await;
+    //     data
+    // }
+
+    //设置杠杆(合约杠杆调整 (SIGNED))
+    pub async fn setting_dual_leverage(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/contract".to_string(),
+                                format!("/private/submit-leverage"),
+                                true,
+                                params,
+        ).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: Value) -> ResponseData
+    {
+        // trace!("login_param:{:?}", self.login_param);
+        //解析账号信息
+        let mut access_key = "".to_string();
+        let mut secret_key = "".to_string();
+        let mut api_memo = "".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") {
+            api_memo = 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 body;
+        let mut headers = HeaderMap::new();
+        headers.insert("Content-Type", "application/json".parse().unwrap());
+
+        let timestamp = Utc::now().timestamp_millis().to_string();
+        // let timestamp = "1589793796145".to_string();
+
+        let params_str = params.to_string();
+        // let mut params_str =  json!({
+        //     "count":"100","price":"8600","symbol":"BTC_USDT"
+        // }).to_string();
+        if requesst_type == "GET" {
+            body = "{}".to_string();
+        } else {
+            body = params_str.clone();
+        }
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      api_memo.clone(),
+                                      params_str.clone(),
+                                      timestamp.clone(),
+                );
+                headers.extend(Self::headers(access_key.clone(), timestamp.clone(), sing.clone()));
+            }
+        }
+
+
+        // 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_str.clone(),
+            body.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("X-BM-KEY", access_key.clone().parse().unwrap());
+        headers.insert("X-BM-SIGN", sign.clone().parse().unwrap());
+        headers.insert("X-BM-TIMESTAMP", timestamp.clone().parse().unwrap());
+        headers
+    }
+    pub fn sign(secret_key: String, api_memo: String, param_str: String, timestamp: String) -> String
+    {
+
+        // X-BM-SIGN= hmac_sha256(Your_api_secret_key, X-BM-TIMESTAMP + '#' +Your_api_memo + '#' + '{"symbol":"BTC_USDT","price":"8600","count":"100"}')
+        let message = format!("{}#{}#{}", timestamp, api_memo, param_str);
+        trace!("1组装数据:\n{}", message);
+        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,
+                       body: String, headers: HeaderMap) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let params_str = RestTool::parse_params_to_str(params.clone());
+        let addrs_url = if params_str.len() > 0 {
+            format!("{}?{}", url.clone(), params_str)
+        } else {
+            format!("{}", url.clone())
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("param-----:???{}",params.clone());
+        trace!("param_str-----:???{}",params_str.clone());
+        trace!("body-----:???{}",body.clone());
+        trace!("headers-----:???{:?}",headers.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(body).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();
+        trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.label.clone(), 200, "success".to_string(), json_value);
+
+        let code = json_value["code"].as_i64();
+        match code {
+            Some(c) => {
+                if c == 1000 {
+                    let default_data = json!({});
+                    let data = json_value.get("data").unwrap_or(default_data.borrow());
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), data.clone())
+                } else {
+                    ResponseData::new(self.tag.clone(), 400, "message".to_string(), json_value)
+                }
+            }
+            None => {
+                ResponseData::new(self.tag.clone(), 400, "message".to_string(), json_value)
+            }
+        }
+    }
+
+    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(d) => {
+                ResponseData::new(self.tag.clone(), 400, format!("base_url {} params {} 错误:{} ", base_url, params, d["message"].as_str().unwrap()), d)
+            }
+            Err(_) => {
+                ResponseData::new(self.tag.clone(), 400, "未知错误".to_string(), text.parse().unwrap())
+            }
+        }
+    }
+}
+
+

+ 459 - 0
exchanges/src/bitmart_swap_ws.rs

@@ -0,0 +1,459 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use chrono::Utc;
+use flate2::bufread::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use once_cell::sync::Lazy;
+use ring::hmac;
+use serde_json::{json, Value};
+use tokio::sync::Mutex;
+use tokio::task;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+pub(crate) static LOGIN_DATA: Lazy<Mutex<(bool, bool)>> = Lazy::new(|| {
+    println!("初始化...");
+    // 0: 需要登录, 1:是否已经登录
+    Mutex::new((false, false))
+});
+
+
+pub enum BitMartSwapWsType {
+    Public,
+    Private,
+}
+
+
+//订阅频道
+#[derive(Clone)]
+pub enum BitMartSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+
+    // // 深度
+    // PuFuturesDepth,
+    // // 公开成交
+    // PuFuturesTrades,
+    // // K线数据
+    // PuFuturesRecords,
+    //
+    // // 订单
+    // PrFuturesOrders,
+    // // 仓位
+    // PrFuturesPositions,
+    // // 余额
+    // PrFuturesBalances,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BitMartSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+    pub api_memo: String,
+}
+
+#[derive(Clone)]
+pub struct BitMartSwapWs {
+    tag: String,                                              // 类型
+    address_url: String,                                        // 地址
+    login_param: Option<BitMartSwapLogin>,                       // 账号
+    symbol_s: Vec<String>,                                      // 币对
+    subscribe_types: Vec<BitMartSwapSubscribeType>,              // 订阅
+    heartbeat_time: u64,                                        // 心跳间隔
+}
+
+
+impl BitMartSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************实例化一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<BitMartSwapLogin>, ws_type: BitMartSwapWsType) -> BitMartSwapWs {
+        return Self::new_with_tag("default-BingxSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+
+    pub fn new_with_tag(tag: String, _is_colo: bool, login_param: Option<BitMartSwapLogin>, ws_type: BitMartSwapWsType) -> BitMartSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            BitMartSwapWsType::Public => {
+                let url = "wss://openapi-ws.bitmart.com/api?protocol=1.1".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+            BitMartSwapWsType::Private => {
+                let url = "wss://openapi-ws.bitmart.com/user?protocol=1.1".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        BitMartSwapWs {
+            tag,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 5,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BitMartSwapSubscribeType>) {
+        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 {
+                BitMartSwapSubscribeType::PuFuturesDepth => false,
+                BitMartSwapSubscribeType::PuFuturesTrades => false,
+                BitMartSwapSubscribeType::PuFuturesRecords => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: BitMartSwapSubscribeType, _login_param: Option<BitMartSwapLogin>) -> String {
+        // let access_key;
+        // let secret_key;
+        // match login_param {
+        //     None => {
+        //         access_key = "".to_string();
+        //         secret_key = "".to_string();
+        //     }
+        //     Some(param) => {
+        //         access_key = param.api_key.clone();
+        //         secret_key = param.secret.clone();
+        //     }
+        // }
+        // let cid = "";
+
+        match subscribe_type {
+            BitMartSwapSubscribeType::PuFuturesDepth => {
+                format!("futures/depth5:{}", symbol.to_uppercase())
+            }
+            BitMartSwapSubscribeType::PuFuturesTrades => {
+                format!("futures/trade:{}", symbol.to_uppercase())
+            }
+            BitMartSwapSubscribeType::PuFuturesRecords => {
+                format!("futures/klineBin1m:{}", symbol.to_uppercase())
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> 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);
+            }
+        }
+        json!({
+            "action":"subscribe",
+            "args":args
+         })
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 login_param = self.login_param.clone();
+        let subscription = self.get_subscription();
+        let address_url = self.address_url.clone();
+        let label = self.tag.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
+
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        let write_tx_clone2 = 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![];
+        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));
+
+            info!("启动连接");
+            loop {
+                info!("BitMart_usdt_swap socket 连接中……");
+                // 需要登录
+                if login_is {
+                    let mut login_data = LOGIN_DATA.lock().await;
+                    let login_param_real = login_param.clone().unwrap();
+                    login_data.0 = true;
+
+                    let timestamp = Utc::now().timestamp_millis().to_string();
+                    let api_key = login_param_real.api_key.clone();
+                    let secret_key = login_param_real.secret.clone();
+                    let api_memo = login_param_real.api_memo.clone();
+
+
+                    // let timestamp = "1589267764859".to_string();
+                    // let api_key = "80618e45710812162b04892c7ee5ead4a3cc3e56".to_string();
+                    // let secret_key = "6c6c98544461bbe71db2bca4c6d7fd0021e0ba9efc215f9c6ad41852df9d9df9".to_string();
+                    // let api_memo = "test001".to_string();
+
+                    let sign = {
+                        let message = format!("{}#{}#bitmart.WebSocket", timestamp.clone(), api_memo);
+                        trace!("组装数据:\n{}", message);
+
+                        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
+                    };
+
+                    trace!("参考sign-3ceeb7e1b8cb165a975e28a2e2dfaca4d30b358873c0351c1a071d8c83314556",);
+                    trace!("自己的sign-{}",sign.clone());
+
+                    let mut args = vec![];
+                    args.push(api_key.clone());
+                    args.push(timestamp.clone());
+                    args.push(sign.clone());
+                    args.push(String::from("web"));
+                    // {"action":"access","args":["<API_KEY>","<timestamp>","<sign>","<dev>"]}
+                    let login_param = json!({
+                        "action": "access",
+                        "args": [
+                           api_key, timestamp.as_str(),sign.as_str(),"web"
+                        ]
+                    });
+                    let login_str = login_param.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;
+                }
+
+                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_sync, Self::message_ping, Self::message_pong, Self::message_binary_sync,
+                ).await;
+                let mut login_data = LOGIN_DATA.lock().await;
+                // 断联后 设置为没有登录
+                login_data.1 = false;
+                info!("BitMart_usdt_swap socket 断连,1s以后重连……");
+                error!("BitMart_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub async fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text).await;
+        Option::from(response_data)
+    }
+    pub fn message_text_sync(text: String) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_text(text))
+        })
+    }
+    //数据解析-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 async fn message_binary(binary: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = Self::parse_zip_data(binary);
+        let response_data = Self::ok_text(message_str).await;
+        Option::from(response_data)
+    }
+    pub fn message_binary_sync(binary: Vec<u8>) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_binary(binary))
+        })
+    }
+    //数据解析
+    pub async fn ok_text(text: String) -> ResponseData
+    {
+        // info!("原始数据:{}", 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();
+
+        // {"action":"access","success":true}
+        let action = json_value["action"].as_str();
+        match action {
+            None => {}
+            Some(r) => {
+                match r {
+                    "access" => {
+                        /*登录响应*/
+                        let success = json_value["success"].as_bool();
+                        match success {
+                            None => {}
+                            Some(s) => {
+                                if s {
+                                    res_data.code = -200;
+                                    res_data.message = "登录成功".to_string();
+                                } else {
+                                    res_data.code = 400;
+                                    res_data.message = format!("登录失败:{}", json_value["error"].as_str().unwrap());
+                                }
+                                return res_data;
+                            }
+                        }
+                    }
+                    "subscribe" => {
+                        /*订阅响应*/
+                        let success = json_value["success"].as_bool();
+                        match success {
+                            None => {}
+                            Some(s) => {
+                                if s {
+                                    res_data.code = -201;
+                                    res_data.message = format!("订阅成功:{}", json_value["request"].clone().to_string());
+                                } else {
+                                    res_data.code = 400;
+                                    res_data.message = format!("订阅失败:{}", json_value["error"].as_str().unwrap());
+                                }
+                                return res_data;
+                            }
+                        }
+                    }
+                    "unsubscribe" => {
+                        /*取消订阅响应*/
+                        let success = json_value["success"].as_bool();
+                        match success {
+                            None => {}
+                            Some(s) => {
+                                if s {
+                                    res_data.code = -201;
+                                    res_data.message = format!("取消-订阅成功:{}", json_value["request"].clone().to_string());
+                                } else {
+                                    res_data.code = 400;
+                                    res_data.message = format!("取消-订阅失败:{}", json_value["error"].as_str().unwrap());
+                                }
+                                return res_data;
+                            }
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        let group = json_value["group"].as_str();
+        match group {
+            Some(ch) => {
+                res_data.code = 200;
+                res_data.data = json_value["data"].clone();
+
+                //订阅数据 甄别
+                if ch.contains("futures/depth") {
+                    res_data.channel = "futures.order_book".to_string();
+                } else if ch.contains("futures/trade") {
+                    res_data.channel = "futures.trades".to_string();
+                } else if ch.contains("futures/klineBin") {
+                    res_data.channel = "futures.candlesticks".to_string();
+                } else {
+                    res_data.channel = "未知推送数据".to_string();
+                }
+
+                return res_data;
+                // match data {
+                //     Some(_) => {
+                //
+                //     }
+                //     None => {
+                //         res_data.channel = format!("{}", ch);
+                //         res_data.code = 400;
+                //         res_data.data = data.clone();
+                //         return res_data;
+                //     }
+                // }
+            }
+            None => {}
+        }
+
+
+        res_data.code = 400;
+        res_data.message = format!("未知响应内容");
+        res_data.data = text.parse().unwrap();
+        trace!("--------------------------------");
+        res_data
+    }
+
+    fn parse_zip_data(p0: Vec<u8>) -> String {
+        // 创建一个GzDecoder的实例,将压缩数据作为输入
+        let mut decoder = GzDecoder::new(&p0[..]);
+
+        // 创建一个缓冲区来存放解压缩后的数据
+        let mut decompressed_data = Vec::new();
+
+        // 读取解压缩的数据到缓冲区中
+        decoder.read_to_end(&mut decompressed_data).expect("解压缩失败");
+        let result = from_utf8(&decompressed_data)
+            .expect("解压缩后的数据不是有效的UTF-8");
+
+        // info!("解压缩数据 {:?}", result);
+        result.to_string()
+    }
+}
+

+ 509 - 0
exchanges/src/bybit_swap_rest.rs

@@ -0,0 +1,509 @@
+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_record(&mut self, symbol: String, interval: String, limit: String) -> ResponseData {
+        let params = serde_json::json!({
+               "category": "linear",
+                "symbol": symbol,
+                "interval": interval,
+                "limit": limit
+         });
+        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 async fn get_user_trades(&mut self, symbol: String, start_time: i64, end_time: i64, limit: i64, cursor: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "category": "linear",
+            "limit": 100
+         });
+        if symbol != "" { params["symbol"] = serde_json::json!(symbol); }
+        if start_time > 0 { params["startTime"] = serde_json::json!(start_time); }
+        if end_time > 0 { params["endTime"] = serde_json::json!(end_time); }
+        if limit > 0 { params["limit"] = serde_json::json!(limit); }
+        if cursor != "" { params["cursor"] = serde_json::json!(cursor); }
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/execution/list".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //账户成交历史
+    pub async fn get_position_closed_pnl(&mut self, symbol: String, start_time: i64, end_time: i64, limit: i64, cursor: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "category": "linear",
+            "limit": 100
+         });
+        if symbol != "" { params["symbol"] = serde_json::json!(symbol); }
+        if start_time > 0 { params["startTime"] = serde_json::json!(start_time); }
+        if end_time > 0 { params["endTime"] = serde_json::json!(end_time); }
+        if limit > 0 { params["limit"] = serde_json::json!(limit); }
+        if cursor != "" { params["cursor"] = serde_json::json!(cursor); }
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/position/closed-pnl".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()
+    }
+}

+ 353 - 0
exchanges/src/bybit_swap_ws.rs

@@ -0,0 +1,353 @@
+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,
+    PuTrade,
+    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=10m".to_string()
+            }
+            BybitSwapWsType::Private => {
+                "wss://stream.bybit.com/v5/private?max_alive_time=10m".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::PuTrade => 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::PuTrade => {
+                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, Self::message_binary).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 message_binary(_po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = format!("Binary:{:?}", _po);
+        Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData {
+        // trace!("原始数据");
+        // 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
+    }
+}

+ 696 - 0
exchanges/src/coinex_swap_rest.rs

@@ -0,0 +1,696 @@
+use std::collections::BTreeMap;
+use std::error::Error;
+use std::time::{SystemTime, UNIX_EPOCH};
+use reqwest::header::{HeaderMap, HeaderValue};
+use hex;
+use reqwest::Client;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use sha2::{Digest, Sha256};
+use tracing::{error};
+
+#[derive(Clone)]
+pub struct CoinexSwapRest {
+    label: String,
+    base_url: String,
+    client: Client,
+    /*******参数*/
+    //登陆所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl CoinexSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(login_param: BTreeMap<String, String>) -> CoinexSwapRest
+    {
+        return CoinexSwapRest::new_label("default-CoinexSwapRest".to_string(), login_param);
+    }
+    pub fn new_label(label: String, login_param: BTreeMap<String, String>) -> CoinexSwapRest
+    {
+        let base_url: String = String::from("https://api.coinex.com");
+
+        /*****返回结构体*******/
+        CoinexSwapRest {
+            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 data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/time".to_string(),
+                                false,
+                                None,
+                                None,
+        ).await;
+        data
+    }
+    //查询个人交易费率
+    pub async fn wallet_fee(&mut self) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/account/trade-fee-rate".to_string(),
+                                true,
+                                None,
+                                None,
+        ).await;
+        data
+    }
+    //查询合约账户
+    pub async fn get_account(&mut self) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/assets/futures/balance".to_string(),
+                                true,
+                                None,
+                                None,
+        ).await;
+        data
+    }
+
+    //查询现货账户
+    pub async fn get_spot_account(&mut self) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/assets/spot/balance".to_string(),
+                                true,
+                                None,
+                                None,
+        ).await;
+        data
+    }
+
+    //指定币对仓位列表
+    pub async fn get_position(&mut self, market: String) -> ResponseData {
+        let params = serde_json::json!({
+            "market": market,
+            "market_type": "FUTURES"
+        });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/pending-position".to_string(),
+                                true,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+
+    //用户仓位列表
+    pub async fn get_user_position(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "market_type": "FUTURES"
+        });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/pending-position".to_string(),
+                                true,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+
+    //获取所有合约交易行情统计 market 市场名列表,多个市场名之间使用英文","分隔,空字符串或不传表示查询全部市场,限制最多10个市场
+    pub async fn get_ticker(&mut self, market: String) -> ResponseData {
+        let params = serde_json::json!({
+            "market": market
+         });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/ticker".to_string(),
+                                false,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+    //查询所有的合约信息
+    pub async fn get_market_details(&mut self, market: String) -> ResponseData {
+        let params = serde_json::json!({
+            "market": market
+         });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/market".to_string(),
+                                false,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+    //查询单个订单详情  /spot/order-status?market=CETUSDT&order_id=13400
+    pub async fn get_order_details(&mut self, order_id: String, market: String) -> ResponseData {
+        let params = serde_json::json!({
+            "market": market,
+            "order_id": order_id
+         });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/order-status".to_string(),
+                                true,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+    //查询未完成合约订单 /futures/pending-order?market=CETUSDT&market_type=FUTURES&side=buy&page=1&limit=10
+    pub async fn get_pending_order(&mut self, client_id: String) -> ResponseData {
+        let params = serde_json::json!({
+            "market_type": "FUTURES",
+            "client_id": client_id,
+            "limit": 10
+         });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/pending-order".to_string(),
+                                true,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+
+    pub async fn get_pending_orders(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "market_type": "FUTURES",
+            "limit": 100
+         });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/pending-order".to_string(),
+                                true,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+
+    pub async fn get_finished_orders(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "market_type": "FUTURES",
+            "limit": 100
+         });
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/finished-order".to_string(),
+                                true,
+                                Some(params.to_string()),
+                                None,
+        ).await;
+        data
+    }
+
+    //下单
+    //  coinex swap 平仓需考虑最小下单量 只能通过close_position和position_id来平仓
+    pub async fn order(&mut self,
+                       market: String,
+                       pos_side: String,
+                       side: String,
+                       size: Decimal,
+                       price: Decimal,
+                       client_id: String,
+    ) -> ResponseData
+    {
+        // 默认为限价单
+        let mut type_y = "limit".to_string();
+        // 0为市价单,
+        if price == Decimal::ZERO {
+            type_y = "market".to_string();
+        }
+        let data;
+
+
+        match format!("{}_{}", pos_side, side).as_str() {
+            "long_buy" => {//开多
+                data = self.swap_order(market, side, type_y, size, price, client_id, false).await;
+            }
+            "long_sell" => {//平多
+                data = self.close_position(market, type_y, price, client_id, false).await;
+            }
+            "short_buy" => {//平空
+                data = self.close_position(market, type_y, price, client_id, false).await;
+            }
+            "short_sell" => {//开空
+                data = self.swap_order(market, side, type_y, size, price, client_id, false).await;
+            }
+            _ => {
+                // 处理未知请求类型
+                error!("下单失败,数量异常! size: {}", size);
+                data = ResponseData::error(self.label.clone(), format!("下单失败, 下单参数: <market: {:?}, pos_side: {:?}, side: {:?}, size: {}, price: {:?}, client_id: {:?}>", market, pos_side, side, size, price, client_id));
+            }
+        };
+        data
+    }
+
+    // 平仓下单
+    pub async fn close_position(&mut self, market: String, type_y: String, price: Decimal, client_id: String, is_hide: bool) -> ResponseData {
+        // 数量不传为全平
+        let param = serde_json::json!({
+            "market":market,
+            "market_type": "FUTURES",
+            "type": type_y,
+            "price":price,
+            "client_id":client_id,
+            "is_hide": is_hide
+        });
+
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/futures/close-position".to_string(),
+                                true,
+                                None,
+                                Some(param.to_string()),
+        ).await;
+        data
+    }
+
+    //合约交易开仓下单
+    pub async fn swap_order(&mut self, market: String, side: String, type_y: String, amount: Decimal, price: Decimal, client_id: String, is_hide: bool) -> ResponseData {
+        let param = serde_json::json!({
+            "market":market,
+            "market_type": "FUTURES",
+            "side": side,
+            "type": type_y,
+            "amount":amount,
+            "price":price,
+            "client_id":client_id,
+            "is_hide": is_hide
+        });
+
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/futures/order".to_string(),
+                                true,
+                                None,
+                                Some(param.to_string()),
+        ).await;
+        data
+    }
+
+    //设置持仓模式
+    pub async fn setting_dual_mode(&mut self) -> ResponseData {
+        ResponseData::error(self.label.clone(), "设置双向持仓失败, coinex没有设置双向持仓".to_string())
+    }
+    //更新双仓模式下的杠杆
+    pub async fn setting_dual_leverage(&mut self, market: String, leverage: i32) -> ResponseData {
+        let params = serde_json::json!({
+                "market": market,
+                "market_type": "FUTURES",
+                // cross: 全仓。全仓模式下,合约账户的全部可用余额都可用作当前全部仓位的共享保证金,系统会使用合约账户中的可用余额自动追加保证金,以避免仓位被强平
+                //isolated: 逐仓。逐仓模式下,仓位保证金不会共享,单个仓位的保证金仅用于当前仓位,系统不会自动追加保证金,需要手动追加。
+                "margin_mode": "cross",
+                "leverage":leverage,
+             });
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/futures/adjust-position-leverage".to_string(),
+                                true,
+                                None,
+                                Some(params.to_string()),
+        ).await;
+        data
+    }
+
+    //撤销单个订单
+    pub async fn cancel_order(&mut self, market: String, order_id: &str, client_id: &str) -> ResponseData {
+        if order_id != "" {  // 如果真实订单id不为空,则用真实订单id取消订单
+            let id = order_id.parse::<i64>().unwrap();
+            let params = serde_json::json!({
+                "market": market,
+                "market_type": "FUTURES",
+                "order_id": id
+            });
+            let data = self.request("POST".to_string(),
+                                    "/v2".to_string(),
+                                    "/futures/cancel-order".to_string(),
+                                    true,
+                                    None,
+                                    Some(params.to_string()),
+            ).await;
+            data
+        } else if client_id != "" {  // 如果客户端id不为空,则用客户端id取消订单
+            let params = serde_json::json!({
+                "market": market,
+                "market_type": "FUTURES",
+                "client_id": client_id
+            });
+
+            let mut data = self.request("POST".to_string(),
+                                        "/v2".to_string(),
+                                        "/futures/cancel-order-by-client-id".to_string(),
+                                        true,
+                                        None,
+                                        Some(params.to_string()),
+            ).await;
+            // 非空的
+            if data.code == 200 && !data.data.is_null() {
+                data.data = data.data.as_array().unwrap()[0]["data"].clone();
+            }
+            data
+        } else {
+            // 否则返回错误
+            error!("取消订单失败失败,id异常");
+            ResponseData::error(self.label.clone(), format!("取消订单失败失败, orderId:{:?}, clientId: {:?} ", order_id, client_id))
+        }
+    }
+
+    // 撤销所有挂单
+    pub async fn cancel_order_all(&mut self, market: String) -> ResponseData {
+        let params = serde_json::json!({
+            "market": market,
+            "market_type": "FUTURES"
+        });
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/futures/cancel-all-order".to_string(),
+                                true,
+                                None,
+                                Some(params.to_string()),
+        ).await;
+        data
+    }
+
+    //查询个人成交记录
+    pub async fn my_trades(&mut self, market: String, limit: i64) -> ResponseData {
+        let mut params = serde_json::json!({
+            "market": market,
+            "market_type": "FUTURES",
+            "limit": 1000
+        });
+        if limit > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
+
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/futures/user-deals".to_string(),
+                                true,
+                                Some(params.to_string()), None).await;
+        data
+    }
+
+    //查询合约账户变更历史
+    pub async fn account_book(&mut self) -> ResponseData {
+        error!("查询合约账户变更历史失败,无实现");
+        ResponseData::error(self.label.clone(), "查询合约账户变更历史失败,接口没实现".to_string())
+    }
+
+
+    //查询子账号列表
+    pub async fn account_get(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+            "is_frozen":false,
+            "page":1,
+            "limit":100,
+        });
+
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/account/subs".to_string(),
+                                true,
+                                Some(params.to_string()), None).await;
+        data
+    }
+
+
+    //根据子账号,生成子账号 APIKEY
+    pub async fn account_subs_api(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/account/subs/api".to_string(),
+                                true,
+                                None,
+                                Some(params.to_string())).await;
+        data
+    }
+
+    //获取子账号 APIKEY 列表
+    pub async fn account_get_apikey(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/account/subs/api".to_string(),
+                                true,
+                                Some(params.to_string()), None).await;
+        data
+    }
+
+    //获取子账号 APIKEY 详情
+    pub async fn account_get_detail(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v2".to_string(),
+                                "/account/subs/api-detail".to_string(),
+                                true,
+                                Some(params.to_string()), None).await;
+        data
+    }
+
+    //编辑子账号 APIKEY
+    pub async fn account_get_update(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/account/subs/edit-api".to_string(),
+                                true,
+                                None,  Some(params.to_string())).await;
+        data
+    }
+
+    //删除子账号 APIKEY
+    pub async fn account_del_apikey(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/v2".to_string(),
+                                "/account/subs/delete-api".to_string(),
+                                true,
+                                None,
+                                Some(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: Option<String>,
+                     body: Option<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 timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis().to_string();
+        // url
+        let mut url_and_query = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let mut headers = HeaderMap::new();
+        headers.insert("Content-Type", HeaderValue::from_static("application/json"));
+        headers.insert(
+            "X-COINEX-KEY",
+            HeaderValue::from_str(&self.login_param.get("access_key").unwrap()).unwrap(),
+        );
+        headers.insert(
+            "X-COINEX-TIMESTAMP",
+            HeaderValue::from_str(&timestamp).unwrap(),
+        );
+
+        if let Some(params) = params {
+            let query = RestTool::parse_params_to_str(params);
+            url_and_query = format!("{}?{}", url_and_query, query);
+        }
+        let body_s = if let Some(body) = body {
+            body
+        } else {
+            "".to_string()
+        };
+
+        //是否需要登陆-- 组装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(
+                    &request_type,
+                    &url_and_query,
+                    &body_s,
+                    timestamp.clone(),
+                    &secret_key,
+                );
+                // trace!("sing:{}", sing);
+                //组装header
+                headers.insert("X-COINEX-SIGN", HeaderValue::from_str(&sing.unwrap()).unwrap());
+            }
+        }
+
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response = self.http_toll(
+            url_and_query,
+            request_type,
+            body_s.clone(),
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        // self.get_delay_info();
+
+        response
+    }
+    fn sign(
+        method: &String,
+        path: &String,
+        body: &String,
+        timestamp: String,
+        secret_key: &String,
+    ) -> Result<String, Box<dyn Error>> {
+        let prepared_str = format!(
+            "{}{}{}{}{}",
+            method, path, body, timestamp, secret_key
+        );
+        let hash = Sha256::digest(prepared_str.as_bytes());
+        Ok(hex::encode(hash))
+    }
+
+    async fn http_toll(&mut self, request_path: String, request_type: String, body: String, headers: HeaderMap) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+
+
+        let request_builder = match request_type.as_str() {
+            "GET" => self.client.get(&url).headers(headers),
+            "POST" => self.client.post(&url).body(body.clone()).headers(headers),
+            "DELETE" => self.client.delete(&url).headers(headers),
+            // "PUT" => self.client.put(url.clone()).json(&params),
+            _ => {
+                panic!("{}", format!("错误的请求类型:{}", request_type.clone()))
+            }
+        };
+
+        // 读取响应的内容
+        let res = request_builder.send().await;
+        match res {
+            Ok(response) => {
+                let is_success = response.status().is_success(); // 先检查状态码
+                let text_result = response.text().await;
+                match text_result {
+                    Ok(text) => {
+                        let data_json_str: Result<Value, serde_json::Error> = serde_json::from_str(text.as_str());
+                        match data_json_str {
+                            Ok(data_json) => {
+                                return if is_success && data_json["code"].to_string() == "0" {
+                                    self.on_success_data(data_json["data"].clone())
+                                } else {
+                                    self.on_error_data(&text, &url, &body)
+                                }
+                            }
+                            Err(e) => {
+                                error!("{} 请求完成,解析响应内容JSON失败 {} {}", url, text.as_str(), e);
+                                self.on_error_data(&e.to_string(), &url, &body)
+                            }
+                        }
+                    }
+                    Err(e) => {
+                        error!("{} 请求完成,解析响应内容失败 {}", url, e);
+                        self.on_error_data(&e.to_string(), &url, &body)
+                    }
+                }
+            }
+            Err(e) => {
+                // 异常情况
+                error!("{} 请求失败,网络错误 {}", url, e);
+                self.on_error_data(&e.to_string(), &url, &body)
+            }
+        }
+    }
+
+    pub fn on_success_data(&mut self, text: Value) -> ResponseData {
+        ResponseData::new(self.label.clone(),
+                          200,
+                          "success".to_string(),
+                          text)
+    }
+
+    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["code"].to_string(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["code"].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
+            }
+        }
+    }
+}

+ 414 - 0
exchanges/src/coinex_swap_ws.rs

@@ -0,0 +1,414 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+use flate2::bufread::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+
+use once_cell::sync::Lazy;  // 使用线程安全的版本
+use hex::encode;
+use serde_json::{json, Value};
+use sha2::{Digest, Sha256};
+use tokio::sync::Mutex;
+use tokio::task;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+// struct LoginData {
+//     pub is_need_login: bool,
+//     pub is_login: bool
+// }
+
+pub(crate) static LOGIN_DATA: Lazy<Mutex<(bool, bool)>> = Lazy::new(|| {
+    println!("初始化...");
+    // 0: 需要登录, 1:是否已经登录
+    Mutex::new((false, false))
+});
+
+//订阅频道
+#[derive(Clone)]
+pub enum CoinexSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesDeals,
+
+    // 订单
+    PrFuturesOrders,
+    // 仓位
+    PrFuturesPositions,
+    // 余额
+    PrFuturesBalances,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct CoinexSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+}
+
+#[derive(Clone)]
+pub struct CoinexSwapWs {
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号信息
+    login_param: Option<CoinexSwapLogin>,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<CoinexSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64
+}
+
+
+impl CoinexSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************实例化一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(login_param: Option<CoinexSwapLogin>) -> CoinexSwapWs {
+        return CoinexSwapWs::new_label("default-CoinexSwapWs".to_string(), login_param);
+    }
+
+    pub fn new_label(label: String, login_param: Option<CoinexSwapLogin>) -> CoinexSwapWs
+    {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = "wss://socket.coinex.com/v2/futures".to_string();
+        info!("走普通通道(不支持colo通道):{}", address_url);
+        CoinexSwapWs {
+            label,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 10
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<CoinexSwapSubscribeType>) {
+        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 {
+                CoinexSwapSubscribeType::PuFuturesDepth => false,
+                CoinexSwapSubscribeType::PuFuturesDeals => false,
+
+                CoinexSwapSubscribeType::PrFuturesOrders => true,
+                CoinexSwapSubscribeType::PrFuturesPositions => true,
+                CoinexSwapSubscribeType::PrFuturesBalances => true,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: CoinexSwapSubscribeType, _login_param: Option<CoinexSwapLogin>) -> Value {
+        // let access_key;
+        // let secret_key;
+        // match login_param {
+        //     None => {
+        //         access_key = "".to_string();
+        //         secret_key = "".to_string();
+        //     }
+        //     Some(param) => {
+        //         access_key = param.api_key.clone();
+        //         secret_key = param.secret.clone();
+        //     }
+        // }
+
+        match subscribe_type {
+            CoinexSwapSubscribeType::PuFuturesDepth => {
+                json!({
+                    "method": "depth.subscribe",
+                    "params": {
+                        "market_list": [
+                            [symbol, 50, "0.000000001", true]
+                        ]
+                    },
+                    "id": 1
+                })
+            }
+            CoinexSwapSubscribeType::PuFuturesDeals => {
+                json!({
+                    "method": "deals.subscribe",
+                    "params": {"market_list": [symbol]},
+                    "id": 1
+                })
+            }
+
+            CoinexSwapSubscribeType::PrFuturesOrders => {
+                json!({
+                  "method": "order.subscribe",
+                  "params": {"market_list": [symbol]},
+                  "id": 1
+                })
+            }
+            CoinexSwapSubscribeType::PrFuturesPositions => {
+                json!({
+                  "method": "position.subscribe",
+                  "params": {"market_list": [symbol]},
+                  "id": 1
+                })
+            }
+            CoinexSwapSubscribeType::PrFuturesBalances => {
+                json!({
+                    "method": "balance.subscribe",
+                    "params": {"ccy_list": ["USDT"]}, // 目前只用u 所以写死
+                    "id": 1
+                })
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<Value> {
+        let mut args = vec![];
+        // 只获取第一个
+        let symbol = self.symbol_s.get(0).unwrap().replace("_", "").to_uppercase();
+
+        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
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 login_param_clone = self.login_param.clone();
+        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);
+        let write_tx_clone2 = Arc::clone(write_tx_am);
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            let ping_str = json!({
+                "method": "server.ping",
+                "params": {},
+                "id": 1
+            });
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Custom(ping_str.to_string()), heartbeat_time).await;
+            trace!("线程-异步心跳-结束");
+        });
+
+        //设置订阅
+        let mut subscribe_array = vec![];
+
+
+        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));
+
+            info!("启动连接");
+            loop {
+                info!("coinex_usdt_swap socket 连接中……");
+                // 需要登录
+                if login_is {
+                    let login_param = login_param_clone.clone().unwrap();
+                    let mut login_data = LOGIN_DATA.lock().await;
+                    login_data.0 = true;
+                    let time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
+                    //登录相关
+                    let prepared_str = format!("{}{}", time, login_param.secret);
+                    // 创建SHA256哈希器实例
+                    let mut hasher = Sha256::new();
+                    // 加密字符串
+                    hasher.update(prepared_str);
+                    // 计算哈希值
+                    let result = hasher.finalize();
+                    // 将哈希值转换为十六进制小写字符串
+                    let hex_str = encode(result).to_lowercase();
+
+                    let login_param = json!({
+                        "method": "server.sign",
+                        "params": {
+                            "access_id": login_param.api_key,
+                            "signed_str": hex_str,
+                            "timestamp": time
+                        },
+                        "id": 1
+                    });
+                    let login_str = login_param.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;
+                } else {
+                    info!("coinex 不需登录");
+                }
+
+                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_sync, Self::message_ping, Self::message_pong, Self::message_binary_sync).await;
+                let mut login_data = LOGIN_DATA.lock().await;
+                // 断联后 设置为没有登录
+                login_data.1 = false;
+                info!("coinex_usdt_swap socket 断连,1s以后重连……");
+                error!("coinex_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub async fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text).await;
+        Option::from(response_data)
+    }
+    pub fn message_text_sync(text: String) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_text(text))
+        })
+    }
+    //数据解析-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 async fn message_binary(binary: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = Self::parse_zip_data(binary);
+        let response_data = Self::ok_text(message_str).await;
+        Option::from(response_data)
+    }
+    pub fn message_binary_sync(binary: Vec<u8>) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_binary(binary))
+        })
+    }
+    //数据解析
+    pub async 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();
+
+        let obj = json_value["method"].as_str();
+        match obj {
+            Some(v)=> {
+                res_data.channel = format!("{}", v);
+                res_data.code = 200;
+                res_data.data = json_value["data"].clone();
+            },
+            None => {
+                // 认证的响应没有method,只能通过id和code判断
+                match json_value["id"].as_i64() {
+                    Some(1) => {
+                        match json_value["code"].as_i64() {
+                            Some(0) =>{
+                                match json_value["data"].as_str() {
+                                    None => {
+                                        // 登录成功逻辑处理
+                                        let mut login_data = LOGIN_DATA.lock().await;
+                                        if login_data.0 { // 需要登录
+                                            if !login_data.1{
+                                                login_data.1 = true;
+                                                res_data.channel = "server.sign".to_string();
+                                                res_data.code = -200;
+                                            }else {
+                                                res_data.code = 400;
+                                            }
+                                        }  else { // 不需要登录
+                                            res_data.code = 200;
+                                        }
+                                    }
+                                    _ =>{
+                                        res_data.code = 400;
+                                    }
+                                }
+                            }
+                            _ => {
+                                res_data.code = 400;
+                            }
+                        }
+                    }
+                    _ => {
+                        res_data.code = 400;
+                    }
+                }
+                res_data.data = json_value;
+            }
+        }
+        res_data
+    }
+
+    fn parse_zip_data(p0: Vec<u8>) -> String{
+        // 创建一个GzDecoder的实例,将压缩数据作为输入
+        let mut decoder = GzDecoder::new(&p0[..]);
+
+        // 创建一个缓冲区来存放解压缩后的数据
+        let mut decompressed_data = Vec::new();
+
+        // 读取解压缩的数据到缓冲区中
+        decoder.read_to_end(&mut decompressed_data).expect("解压缩失败");
+        let result = from_utf8(&decompressed_data)
+            .expect("解压缩后的数据不是有效的UTF-8");
+
+        // info!("解压缩数据 {:?}", result);
+        result.to_string()
+    }
+}
+

+ 313 - 0
exchanges/src/coinsph_swap_rest.rs

@@ -0,0 +1,313 @@
+use std::collections::BTreeMap;
+// use chrono::Utc;
+use reqwest::header::HeaderMap;
+use reqwest::{Client};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::{error, info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use serde_json::{json, Value};
+
+#[derive(Clone, Debug)]
+pub struct CoinsphSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl CoinsphSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> CoinsphSwapRest
+    {
+        return CoinsphSwapRest::new_with_tag("default-CoinsphSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> CoinsphSwapRest {
+        let base_url = if is_colo {
+            "https://api.pro.coins.ph".to_string()
+        } else {
+            "https://api.pro.coins.ph".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        CoinsphSwapRest {
+            tag,
+            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 = json!({});
+        let data = self.request("GET".to_string(),
+                                "/openapi/v1".to_string(),
+                                "/time".to_string(),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+    //获取合约信息
+    pub async fn get_market(&mut self,params:Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/openapi/v1".to_string(),
+                                "/exchangeInfo".to_string(),
+                                false,
+                                params,
+        ).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: Value) -> 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 timestamp = Utc::now().timestamp_millis();
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value);
+
+        // let serverTime = json_value["serverTime"].as_i64();
+        // match serverTime {
+        //     None => {}
+        //     Some(v) => {
+        //            }
+        // }
+        return ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value.clone());
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 400 - 0
exchanges/src/coinsph_swap_ws.rs

@@ -0,0 +1,400 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use chrono::Utc;
+use flate2::bufread::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use once_cell::sync::Lazy;
+use ring::hmac;
+use serde_json::{json, Value};
+use tokio::sync::Mutex;
+use tokio::task;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+pub(crate) static LOGIN_DATA: Lazy<Mutex<(bool, bool)>> = Lazy::new(|| {
+    println!("初始化...");
+    // 0: 需要登录, 1:是否已经登录
+    Mutex::new((false, false))
+});
+
+
+pub enum CoinsphSwapWsType {
+    Public,
+    Private,
+}
+
+
+//订阅频道
+#[derive(Clone)]
+pub enum CoinsphSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+
+    // // 深度
+    // PuFuturesDepth,
+    // // 公开成交
+    // PuFuturesTrades,
+    // // K线数据
+    // PuFuturesRecords,
+    //
+    // // 订单
+    // PrFuturesOrders,
+    // // 仓位
+    // PrFuturesPositions,
+    // // 余额
+    // PrFuturesBalances,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct CoinsphSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+    pub api_memo: String,
+}
+
+#[derive(Clone)]
+pub struct CoinsphSwapWs {
+    tag: String,
+    // 类型
+    address_url: String,
+    // 地址
+    login_param: Option<CoinsphSwapLogin>,
+    // 账号
+    symbol_s: Vec<String>,
+    // 币对
+    subscribe_types: Vec<CoinsphSwapSubscribeType>,
+    // 订阅
+    heartbeat_time: u64,                                        // 心跳间隔
+}
+
+
+impl CoinsphSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************实例化一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<CoinsphSwapLogin>, ws_type: CoinsphSwapWsType) -> CoinsphSwapWs {
+        return Self::new_with_tag("default-BingxSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+
+    pub fn new_with_tag(tag: String, _is_colo: bool, login_param: Option<CoinsphSwapLogin>, ws_type: CoinsphSwapWsType) -> CoinsphSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            CoinsphSwapWsType::Public => {
+                let url = "wss://wsapi.pro.coins.ph/openapi/quote/stream".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+            CoinsphSwapWsType::Private => {
+                let url = "wss://wsapi.pro.coins.ph/user?protocol=1.1".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        CoinsphSwapWs {
+            tag,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 5,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<CoinsphSwapSubscribeType>) {
+        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("_", "");
+        }
+        self.symbol_s = b_array;
+    }
+    //频道是否需要登录
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                CoinsphSwapSubscribeType::PuFuturesDepth => false,
+                CoinsphSwapSubscribeType::PuFuturesTrades => false,
+                CoinsphSwapSubscribeType::PuFuturesRecords => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: CoinsphSwapSubscribeType, _login_param: Option<CoinsphSwapLogin>) -> String {
+        // let access_key;
+        // let secret_key;
+        // match login_param {
+        //     None => {
+        //         access_key = "".to_string();
+        //         secret_key = "".to_string();
+        //     }
+        //     Some(param) => {
+        //         access_key = param.api_key.clone();
+        //         secret_key = param.secret.clone();
+        //     }
+        // }
+        // let cid = "";
+
+        match subscribe_type {
+            CoinsphSwapSubscribeType::PuFuturesDepth => {//深度
+                format!("{}@depth5", symbol)
+            }
+            CoinsphSwapSubscribeType::PuFuturesTrades => {//公开成交
+                format!("{}@aggTrade", symbol)
+            }
+            CoinsphSwapSubscribeType::PuFuturesRecords => {//k线数据
+                format!("{}@kline_1m", symbol)
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> 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);
+            }
+        }
+        json!({
+             "method": "SUBSCRIBE",
+             "params": args,
+              "id": 1
+         })
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 login_param = self.login_param.clone();
+        let subscription = self.get_subscription();
+        let address_url = self.address_url.clone();
+        let label = self.tag.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
+
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        let write_tx_clone2 = 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![];
+        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));
+
+            info!("启动连接");
+            loop {
+                info!("coinsph_usdt_swap socket 连接中……");
+                // 需要登录
+                if login_is {
+                    let mut login_data = LOGIN_DATA.lock().await;
+                    let login_param_real = login_param.clone().unwrap();
+                    login_data.0 = true;
+
+                    let timestamp = Utc::now().timestamp_millis().to_string();
+                    let api_key = login_param_real.api_key.clone();
+                    let secret_key = login_param_real.secret.clone();
+                    let api_memo = login_param_real.api_memo.clone();
+
+
+                    // let timestamp = "1589267764859".to_string();
+                    // let api_key = "80618e45710812162b04892c7ee5ead4a3cc3e56".to_string();
+                    // let secret_key = "6c6c98544461bbe71db2bca4c6d7fd0021e0ba9efc215f9c6ad41852df9d9df9".to_string();
+                    // let api_memo = "test001".to_string();
+
+                    let sign = {
+                        let message = format!("{}#{}#Coinsph.WebSocket", timestamp.clone(), api_memo);
+                        trace!("组装数据:\n{}", message);
+
+                        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
+                    };
+
+                    trace!("参考sign-3ceeb7e1b8cb165a975e28a2e2dfaca4d30b358873c0351c1a071d8c83314556",);
+                    trace!("自己的sign-{}",sign.clone());
+
+                    let mut args = vec![];
+                    args.push(api_key.clone());
+                    args.push(timestamp.clone());
+                    args.push(sign.clone());
+                    args.push(String::from("web"));
+                    // {"action":"access","args":["<API_KEY>","<timestamp>","<sign>","<dev>"]}
+                    let login_param = json!({
+                        "action": "access",
+                        "args": [
+                           api_key, timestamp.as_str(),sign.as_str(),"web"
+                        ]
+                    });
+                    let login_str = login_param.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;
+                }
+
+                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_sync, Self::message_ping, Self::message_pong, Self::message_binary_sync,
+                ).await;
+                let mut login_data = LOGIN_DATA.lock().await;
+                // 断联后 设置为没有登录
+                login_data.1 = false;
+                info!("coinsph_usdt_swap socket 断连,1s以后重连……");
+                error!("coinsph_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub async fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text).await;
+        Option::from(response_data)
+    }
+    pub fn message_text_sync(text: String) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_text(text))
+        })
+    }
+    //数据解析-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 async fn message_binary(binary: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = Self::parse_zip_data(binary);
+        let response_data = Self::ok_text(message_str).await;
+        Option::from(response_data)
+    }
+    pub fn message_binary_sync(binary: Vec<u8>) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_binary(binary))
+        })
+    }
+    //数据解析
+    pub async fn ok_text(text: String) -> ResponseData
+    {
+        // info!("原始数据:{}", 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();
+
+        let id = json_value["id"].as_str();
+        match id {
+            Some(_v) => {
+                res_data.code = -201;
+                res_data.message = format!("订阅成功:{}", json_value["request"].clone().to_string());
+                return res_data;
+            }
+            None => {}
+        }
+
+
+        let e = json_value["e"].as_str();
+        match e {
+            None => {}
+            Some(v) => {
+                res_data.code = 200;
+                res_data.data = json_value.clone();
+                if v.contains("depth") {
+                    res_data.channel = "futures.order_book".to_string();
+                } else if v.contains("aggTrade") {
+                    res_data.channel = "futures.trades".to_string();
+                } else if v.contains("kline") {
+                    res_data.channel = "futures.candlesticks".to_string();
+                } else {
+                    res_data.code = 400;
+                    res_data.channel = "未知推送数据".to_string();
+                }
+                return res_data;
+            }
+        }
+
+        res_data.code = 400;
+        res_data.message = format!("未知响应内容");
+        res_data.data = text.parse().unwrap();
+        trace!("--------------------------------");
+        res_data
+    }
+
+    fn parse_zip_data(p0: Vec<u8>) -> String {
+        // 创建一个GzDecoder的实例,将压缩数据作为输入
+        let mut decoder = GzDecoder::new(&p0[..]);
+
+        // 创建一个缓冲区来存放解压缩后的数据
+        let mut decompressed_data = Vec::new();
+
+        // 读取解压缩的数据到缓冲区中
+        decoder.read_to_end(&mut decompressed_data).expect("解压缩失败");
+        let result = from_utf8(&decompressed_data)
+            .expect("解压缩后的数据不是有效的UTF-8");
+
+        // info!("解压缩数据 {:?}", result);
+        result.to_string()
+    }
+}
+

+ 307 - 0
exchanges/src/cointr_swap_rest.rs

@@ -0,0 +1,307 @@
+use std::collections::BTreeMap;
+
+use reqwest::Client;
+// use chrono::Utc;
+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::{error, info, trace};
+
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+
+#[derive(Clone, Debug)]
+pub struct CointrSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl CointrSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> CointrSwapRest
+    {
+        return CointrSwapRest::new_with_tag("default-CointrSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> CointrSwapRest {
+        let base_url = if is_colo {
+            "https://api.cointr.pro".to_string()
+        } else {
+            "https://api.cointr.pro".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        CointrSwapRest {
+            tag,
+            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 = json!({});
+    //     let data = self.request("GET".to_string(),
+    //                             "/v1".to_string(),
+    //                             "/public/system_info".to_string(),
+    //                             false,
+    //                             params,
+    //     ).await;
+    //     data
+    // }
+    //获取合约信息( 合同规格)
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v1".to_string(),
+                                "/futures/public/instruments".to_string(),
+                                false,
+                                params,
+        ).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: Value) -> 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 timestamp = Utc::now().timestamp_millis();
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        return ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value.clone());
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 434 - 0
exchanges/src/cointr_swap_ws.rs

@@ -0,0 +1,434 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use chrono::Utc;
+use flate2::bufread::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use once_cell::sync::Lazy;
+use ring::hmac;
+use serde_json::{json, Value};
+use tokio::sync::Mutex;
+use tokio::task;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode};
+
+pub(crate) static LOGIN_DATA: Lazy<Mutex<(bool, bool)>> = Lazy::new(|| {
+    println!("初始化...");
+    // 0: 需要登录, 1:是否已经登录
+    Mutex::new((false, false))
+});
+
+
+pub enum CointrSwapWsType {
+    PublicAndPrivate
+}
+
+
+//订阅频道
+#[derive(Clone)]
+pub enum CointrSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+
+    // // 深度
+    // PuFuturesDepth,
+    // // 公开成交
+    // PuFuturesTrades,
+    // // K线数据
+    // PuFuturesRecords,
+    //
+    // // 订单
+    // PrFuturesOrders,
+    // // 仓位
+    // PrFuturesPositions,
+    // // 余额
+    // PrFuturesBalances,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct CointrSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+    pub api_memo: String,
+}
+
+#[derive(Clone)]
+pub struct CointrSwapWs {
+    tag: String,
+    // 类型
+    address_url: String,
+    // 地址
+    login_param: Option<CointrSwapLogin>,
+    // 账号
+    symbol_s: Vec<String>,
+    // 币对
+    subscribe_types: Vec<CointrSwapSubscribeType>,
+    // 订阅
+    _heartbeat_time: u64,                                        // 心跳间隔
+}
+
+
+impl CointrSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************实例化一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<CointrSwapLogin>, ws_type: CointrSwapWsType) -> CointrSwapWs {
+        return Self::new_with_tag("default-BingxSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+
+    pub fn new_with_tag(tag: String, _is_colo: bool, login_param: Option<CointrSwapLogin>, ws_type: CointrSwapWsType) -> CointrSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            CointrSwapWsType::PublicAndPrivate => {
+                let url = "wss://stream.cointr.pro/ws".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        CointrSwapWs {
+            tag,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            _heartbeat_time: 1000 * 5,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<CointrSwapSubscribeType>) {
+        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 {
+                CointrSwapSubscribeType::PuFuturesDepth => false,
+                CointrSwapSubscribeType::PuFuturesTrades => false,
+                CointrSwapSubscribeType::PuFuturesRecords => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: CointrSwapSubscribeType, _login_param: Option<CointrSwapLogin>) -> String {
+        match subscribe_type {
+            CointrSwapSubscribeType::PuFuturesDepth => {//深度
+                json!({
+                    "op":"subscribe",
+                    "channel":"books_perp",
+                    "args":{
+                        "instId":symbol,
+                        "":"0.001"
+                    },
+                }).to_string()
+            }
+            CointrSwapSubscribeType::PuFuturesTrades => {//公开成交
+                json!({
+                    "op":"subscribe",
+                    "channel":"trades_perp",
+                    "args":{
+                        "instId":symbol
+                    },
+                }).to_string()
+            }
+            CointrSwapSubscribeType::PuFuturesRecords => {//k线数据
+                json!({
+                    "op":"subscribe",
+                    "channel":"kline_perp",
+                    "args":{
+                        "instId":symbol,
+                        "bar":"1M"
+                    },
+                }).to_string()
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<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(),
+                                                  self.login_param.clone(),
+                );
+                args.push(ty_str);
+            }
+        }
+
+        return args;
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 login_param = self.login_param.clone();
+        let subscription = self.get_subscription();
+        let address_url = self.address_url.clone();
+        let label = self.tag.clone();
+        // let heartbeat_time = self.heartbeat_time.clone();
+
+
+        //心跳-- 方法内部线程启动
+        // let write_tx_clone1 = Arc::clone(write_tx_am);
+        let write_tx_clone2 = 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![];
+        for su in subscription {
+            subscribe_array.push(su);
+        }
+
+
+        //链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            info!("启动连接");
+            loop {
+                info!("Cointr_usdt_swap socket 连接中……");
+                // 需要登录
+                if login_is {
+                    let mut login_data = LOGIN_DATA.lock().await;
+                    let login_param_real = login_param.clone().unwrap();
+                    login_data.0 = true;
+
+                    let timestamp = Utc::now().timestamp_millis().to_string();
+                    let api_key = login_param_real.api_key.clone();
+                    let secret_key = login_param_real.secret.clone();
+                    let api_memo = login_param_real.api_memo.clone();
+
+
+                    // let timestamp = "1589267764859".to_string();
+                    // let api_key = "80618e45710812162b04892c7ee5ead4a3cc3e56".to_string();
+                    // let secret_key = "6c6c98544461bbe71db2bca4c6d7fd0021e0ba9efc215f9c6ad41852df9d9df9".to_string();
+                    // let api_memo = "test001".to_string();
+
+                    let sign = {
+                        let message = format!("{}#{}#Cointr.WebSocket", timestamp.clone(), api_memo);
+                        trace!("组装数据:\n{}", message);
+
+                        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
+                    };
+
+
+                    let mut args = vec![];
+                    args.push(api_key.clone());
+                    args.push(timestamp.clone());
+                    args.push(sign.clone());
+                    args.push(String::from("web"));
+                    // {"action":"access","args":["<API_KEY>","<timestamp>","<sign>","<dev>"]}
+                    let login_param = json!({
+                        "action": "access",
+                        "args": [
+                           api_key, timestamp.as_str(),sign.as_str(),"web"
+                        ]
+                    });
+                    let login_str = login_param.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;
+                }
+
+                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_sync, Self::message_ping, Self::message_pong, Self::message_binary_sync,
+                ).await;
+                let mut login_data = LOGIN_DATA.lock().await;
+                // 断联后 设置为没有登录
+                login_data.1 = false;
+                info!("Cointr_usdt_swap socket 断连,1s以后重连……");
+                error!("Cointr_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub async fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text).await;
+        Option::from(response_data)
+    }
+    pub fn message_text_sync(text: String) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_text(text))
+        })
+    }
+    //数据解析-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 async fn message_binary(binary: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = Self::parse_zip_data(binary);
+        let response_data = Self::ok_text(message_str).await;
+        Option::from(response_data)
+    }
+    pub fn message_binary_sync(binary: Vec<u8>) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_binary(binary))
+        })
+    }
+    //数据解析
+    pub async fn ok_text(text: String) -> ResponseData
+    {
+        // info!("原始数据:{}", 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();
+
+        let code = json_value["code"].as_i64();
+        match code {
+            None => {}
+            Some(_) => {
+                let msg = json_value["msg"].as_str().unwrap();
+                match msg {
+                    "connect.success" => {
+                        res_data.code = -201;
+                        res_data.message = format!("连接成功:");
+                        return res_data;
+                    }
+                    "subscribe.faild" => {
+                        res_data.code = 400;
+                        res_data.message = format!("订阅失败:{}", msg);
+                        return res_data;
+                    }
+                    _ => {}
+                }
+            }
+        }
+        let event = json_value["event"].as_str();
+        match event {
+            None => {}
+            Some(v) => {
+                match v {
+                    "subscribe" => {
+                        res_data.code = -201;
+                        res_data.message = format!("订阅成功:{}", json_value.clone().to_string());
+                        return res_data;
+                    }
+                    "error" => {
+                        res_data.code = 400;
+                        res_data.message = format!("订阅失败:{}", json_value["msg"].clone().to_string());
+                        return res_data;
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+
+        let channel = json_value["channel"].as_str();
+        match channel {
+            None => {}
+            Some(c) => {
+                let action = json_value["action"].as_str().unwrap();
+                if action.contains("update") {
+                    res_data.code = 200;
+                    res_data.data = json_value.clone();
+                    if c.contains("books_perp") {
+                        res_data.channel = "futures.order_book".to_string();
+                    } else if c.contains("trades_perp") {
+                        res_data.channel = "futures.trades".to_string();
+                    } else if c.contains("kline_perp") {
+                        res_data.channel = "futures.candlesticks".to_string();
+                    } else {
+                        res_data.code = 400;
+                        res_data.channel = "未知推送数据".to_string();
+                    }
+                }else{
+                    res_data.code = 400;
+                    res_data.channel = format!("{}类型数据",action);
+                }
+                return res_data;
+            }
+        }
+
+        res_data.code = 400;
+        res_data.message = format!("未知响应内容");
+        res_data.data = text.parse().unwrap();
+        trace!("--------------------------------");
+        res_data
+    }
+
+    fn parse_zip_data(p0: Vec<u8>) -> String {
+        // 创建一个GzDecoder的实例,将压缩数据作为输入
+        let mut decoder = GzDecoder::new(&p0[..]);
+
+        // 创建一个缓冲区来存放解压缩后的数据
+        let mut decompressed_data = Vec::new();
+
+        // 读取解压缩的数据到缓冲区中
+        decoder.read_to_end(&mut decompressed_data).expect("解压缩失败");
+        let result = from_utf8(&decompressed_data)
+            .expect("解压缩后的数据不是有效的UTF-8");
+
+        // info!("解压缩数据 {:?}", result);
+        result.to_string()
+    }
+}
+

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

+ 615 - 0
exchanges/src/gate_swap_rest.rs

@@ -0,0 +1,615 @@
+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, last_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "contract":contract,
+            "last_id": last_id,
+            "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
+            }
+        }
+    }
+}

+ 365 - 0
exchanges/src/gate_swap_ws.rs

@@ -0,0 +1,365 @@
+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, Self::message_binary).await;
+
+                error!("gate_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text);
+        Option::from(response_data)
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -300, "success".to_string(), Value::Null));
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null));
+    }
+    //数据解析-二进制
+    pub fn message_binary(_po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = format!("Binary:{:?}", _po);
+        Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData
+    {
+        // trace!("原始数据:{}", text);
+        let mut res_data = ResponseData::new("".to_string(), 200, "success".to_string(), Value::Null);
+        let json_value: Value = serde_json::from_str(&text).unwrap();
+
+        if json_value["channel"].as_str() == Option::from("futures.pong") {
+            res_data.code = -301;
+            res_data.message = "success".to_string();
+        } else if json_value.get("error").is_some() {
+            let message = json_value["error"]["message"].as_str().unwrap().to_string();
+            let mes = message.trim_end_matches('\n');
+
+            res_data.code = 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
+            }
+        }
+    }
+}

+ 508 - 0
exchanges/src/htx_swap_rest.rs

@@ -0,0 +1,508 @@
+use std::collections::BTreeMap;
+
+use chrono::Utc;
+use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
+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::{error, info, trace};
+
+use crate::response_base::ResponseData;
+
+// 定义用于 URL 编码的字符集
+pub const FRAGMENT: &AsciiSet = &CONTROLS
+    .add(b' ')
+    .add(b':')
+    .add(b'=')
+    .add(b'+')
+    .add(b'/').add(b'?').add(b'#')
+    .add(b'[').add(b']').add(b'@').add(b'!').add(b'$').add(b'&')
+    .add(b'\'').add(b'(').add(b')').add(b'*').add(b',')
+    .add(b';').add(b'"').add(b'%')
+;
+
+
+#[derive(Clone)]
+pub struct HtxSwapRest {
+    label: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl HtxSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> HtxSwapRest
+    {
+        return HtxSwapRest::new_label("default-HtxSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> HtxSwapRest
+    {
+        let base_url: String = String::from("https://api.hbdm.vn");
+        info!("走普通通道:{}",base_url);
+
+        if is_colo {} else {}
+        /*****返回结构体*******/
+        HtxSwapRest {
+            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/v1".to_string(),
+                                format!("/timestamp"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //行情 (【通用】获取聚合行情)
+    pub async fn get_ticker(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/linear-swap-ex/market".to_string(),
+                                format!("/detail/merged"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+    //合约信息 (【通用】获取合约信息)
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_contract_info"),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+    //查询合约账户 (【全仓】获取用户账户信息)
+    pub async fn get_account(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_account_info"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //用户仓位列表(【全仓】获取用户持仓信息)
+    pub async fn get_user_position(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_position_info"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //查询合约订单列表 (【全仓】获取合约订单信息)
+    pub async fn get_orders(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_order_info"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //查询单个订单详情 (【全仓】获取订单明细信息)
+    pub async fn get_order_details(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_order_info"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //合约交易下单 (【全仓】合约下单)
+    pub async fn swap_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_order"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    // 全部撤单(【全仓】全部撤单)
+    pub async fn cancel_price_order(&mut self, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/linear-swap-api/v1".to_string(),
+                     format!("/swap_cross_cancelall"),
+                     true,
+                     params,
+        ).await
+    }
+
+    //撤销单个订单 (【全仓】撤销订单)
+    pub async fn cancel_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_cancel"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //设置持仓模式 (【全仓】切换持仓模式)
+    pub async fn setting_dual_mode(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_switch_position_mode"),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //设置杠杆(【全仓】切换杠杆)
+    pub async fn setting_dual_leverage(&mut self, params: Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/linear-swap-api/v1".to_string(),
+                                format!("/swap_cross_switch_lever_rate"),
+                                true,
+                                params,
+        ).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: Value) -> 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 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());
+        }
+
+        let utc_now = Utc::now();
+        let timestamp = utc_now.format("%Y-%m-%dT%H:%M:%S").to_string();
+        let mut params_str = "".to_string();
+        //是否需要登录-- 组装sing
+        let mut sing = "".to_string();
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {//需要登录-且登录参数齐全
+                let mut login_param = serde_json::json!({});
+                login_param["AccessKeyId"] = Value::from(access_key);
+                login_param["SignatureMethod"] = Value::from("HmacSHA256");
+                login_param["SignatureVersion"] = Value::from("2");
+                login_param["Timestamp"] = Value::from(timestamp);
+
+                //如果是get 所有参所也要参与校验,如果是post 只有校验参数需要校验
+                let mut verify_param = serde_json::json!({});
+                if requesst_type == "GET" {
+                    merge_json(&mut verify_param, &params);
+                    merge_json(&mut verify_param, &login_param);
+                    body = "{}".to_string();
+                } else if requesst_type == "POST" {
+                    merge_json(&mut verify_param, &login_param);
+                    body = params.to_string();
+                }
+                trace!("需要校验参数:\n{}", verify_param.to_string());
+
+
+                //4. ASCII码的顺序对参数名进行排序
+                let verify_param_str = json_to_query_string(verify_param);
+                trace!("排序后的拼接参数:\n{}", verify_param_str);
+                params_str = verify_param_str.to_string().clone();
+
+                //组装sing
+                sing = Self::sign(secret_key.clone(),
+                                  requesst_type.clone(),
+                                  prefix_url.clone(),
+                                  request_url.clone(),
+                                  verify_param_str.clone(),
+                );
+                // sing = utf8_percent_encode(sing.as_str(), FRAGMENT).to_string();
+                // sing = sing.to_uppercase();
+                sing = utf8_percent_encode(&sing, FRAGMENT).to_string();
+                trace!("sing:{}", sing);
+                // ?AccessKeyId=e2xxxxxx-99xxxxxx-84xxxxxx-7xxxx&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2017-05-11T15%3A19%3A30&Signature=    4F65x5A2bLyMWVQj3Aqp%2BB4w%2BivaA7n5Oi2SuYtCJ9o%3D
+                // ?AccessKeyId=58edb6bb-qz5c4v5b6n-24498508-c5919&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2024-04-29T07%3A26%3A39&Signature=  R0SAEWSEC+A6HS5URSHZIFOZBYQBRCLH0DTDSAL0HLS=
+                //                                                                                                                                             4F65x5A2bLyMWVQj3Aqp+B4w+ivaA7n5Oi2SuYtCJ9o=
+            }
+        } else {
+            if requesst_type == "GET" {
+                let verify_param_str = json_to_query_string(params);
+                params_str = verify_param_str.to_string().clone();
+                body = "{}".to_string();
+            } else if requesst_type == "POST" {
+                body = params.to_string();
+            }
+        }
+
+        // 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_str,
+            body.clone(),
+            sing.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,
+                verify_param: String) -> String
+    {
+        let message = format!("{}\napi.hbdm.vn\n{}{}\n{}",
+                              requesst_type,
+                              prefix_url, request_url,
+                              verify_param
+        );
+        // trace!("**********", );
+        trace!("组装数据:\n{}", message);
+        // trace!("**********", );
+
+        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
+    }
+
+
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: String,
+                       sing: String, headers: HeaderMap) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let params_str = if sing.len() > 0 {
+            format!("{}&Signature={}", params.clone(), sing)
+        } else {
+            format!("{}", params.clone())
+        };
+        let addrs_url = if params_str.len() > 0 {
+            format!("{}?{}", url.clone(), params_str)
+        } else {
+            format!("{}", url.clone())
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("param-----:???{}",params.clone());
+        trace!("param_str-----:???{}",params_str.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.label.clone(), 200, "success".to_string(), json_value);
+
+        let status = json_value["status"].as_str().unwrap();
+        match status {
+            "ok" => {
+                //判断是否有code ,没有表示特殊接口,直接返回
+                if json_value.get("data").is_some() {
+                    let data = json_value.get("data").unwrap();
+                    ResponseData::new(self.label.clone(), 200, "success".to_string(), data.clone())
+                } else {
+                    ResponseData::new(self.label.clone(), 200, "success".to_string(), json_value)
+                }
+            }
+            _ => {
+                ResponseData::new(self.label.clone(), 400, "error".to_string(), json_value)
+            }
+        }
+    }
+
+    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
+            }
+        }
+    }
+}
+
+
+// 合并两个 JSON 对象的函数
+fn merge_json(a: &mut Value, b: &Value) {
+    match (a, b) {
+        (Value::Object(ref mut a_map), Value::Object(b_map)) => {
+            for (k, v) in b_map {
+                merge_json(a_map.entry(k.clone()).or_insert(Value::Null), v);
+            }
+        }
+        (a, b) => {
+            *a = b.clone();
+        }
+    }
+}
+
+
+// 函数:将 JSON 对象转换为排序后的查询字符串
+fn json_to_query_string(value: Value) -> String {
+    let mut params = BTreeMap::new();
+
+    if let Value::Object(obj) = value {
+        for (k, v) in obj {
+            // 确保只处理字符串值
+            if let Value::String(v_str) = v {
+                params.insert(k, v_str);
+            } else {
+                // 对于非字符串值,我们可以转换为字符串或执行其他处理
+                params.insert(k, v.to_string());
+            }
+        }
+    }
+
+    // 拼接键值对为查询字符串
+    params.iter()
+        .map(|(k, v)| format!("{}={}", utf8_percent_encode(k, FRAGMENT), utf8_percent_encode(v, FRAGMENT)))
+        .collect::<Vec<String>>()
+        .join("&")
+}

+ 467 - 0
exchanges/src/htx_swap_ws.rs

@@ -0,0 +1,467 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use chrono::Utc;
+use flate2::bufread::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use once_cell::sync::Lazy;
+use ring::hmac;
+use serde_json::{json, Value};
+use tokio::sync::Mutex;
+use tokio::task;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::AbstractWsMode;
+
+pub(crate) static LOGIN_DATA: Lazy<Mutex<(bool, bool)>> = Lazy::new(|| {
+    println!("初始化...");
+    // 0: 需要登录, 1:是否已经登录
+    Mutex::new((false, false))
+});
+
+
+pub enum HtxSwapWsType {
+    Public,
+    Private,
+}
+
+
+//订阅频道
+#[derive(Clone)]
+pub enum HtxSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // // 公开成交
+    // PuFuturesDeals,
+
+    // 订单
+    PrFuturesOrders,
+    // 仓位
+    PrFuturesPositions,
+    // 余额
+    PrFuturesBalances,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct HtxSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+}
+
+#[derive(Clone)]
+pub struct HtxSwapWs {
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号信息
+    login_param: Option<HtxSwapLogin>,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<HtxSwapSubscribeType>,
+    //心跳间隔
+    _heartbeat_time: u64,
+}
+
+
+impl HtxSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************实例化一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(login_param: Option<HtxSwapLogin>, ws_type: HtxSwapWsType) -> HtxSwapWs {
+        return HtxSwapWs::new_label("default-HtxSwapWs".to_string(), login_param, ws_type);
+    }
+
+    pub fn new_label(label: String, login_param: Option<HtxSwapLogin>, ws_type: HtxSwapWsType) -> HtxSwapWs
+    {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            HtxSwapWsType::Public => {
+                let url = "wss://api.hbdm.vn/linear-swap-ws".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+            HtxSwapWsType::Private => {
+                let url = "wss://api.hbdm.vn/linear-swap-notification".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        HtxSwapWs {
+            label,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            _heartbeat_time: 1000 * 10,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<HtxSwapSubscribeType>) {
+        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 {
+                HtxSwapSubscribeType::PuFuturesDepth => false,
+                // HtxSwapSubscribeType::PuFuturesDeals => false,
+                //
+                HtxSwapSubscribeType::PrFuturesOrders => true,
+                HtxSwapSubscribeType::PrFuturesPositions => true,
+                HtxSwapSubscribeType::PrFuturesBalances => true,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: HtxSwapSubscribeType, _login_param: Option<HtxSwapLogin>) -> Value {
+        // let access_key;
+        // let secret_key;
+        // match login_param {
+        //     None => {
+        //         access_key = "".to_string();
+        //         secret_key = "".to_string();
+        //     }
+        //     Some(param) => {
+        //         access_key = param.api_key.clone();
+        //         secret_key = param.secret.clone();
+        //     }
+        // }
+        // let cid = "";
+
+        match subscribe_type {
+            HtxSwapSubscribeType::PuFuturesDepth => {
+                json!({
+                      "sub":format!("market.{}.depth.step0", symbol.to_uppercase()),
+                      "id": "id5"
+                })
+            }
+
+            HtxSwapSubscribeType::PrFuturesOrders => {
+                json!({
+                        "op":"sub",
+                        "topic":format!("orders_cross.{}", symbol.to_lowercase())
+                })
+            }
+            HtxSwapSubscribeType::PrFuturesPositions => {
+                json!({
+                        "op":"sub",
+                        "topic":format!("positions_cross.{}", symbol.to_uppercase())
+                })
+            }
+            HtxSwapSubscribeType::PrFuturesBalances => {
+                json!({
+                        "op":"sub",
+                        "topic":format!("accounts_cross.USDT")
+                })
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<Value> {
+        let mut args = vec![];
+        // 只获取第一个
+        // let symbol = self.symbol_s.get(0).unwrap().replace("_", "-");
+        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
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 login_param = self.login_param.clone();
+        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);
+        let write_tx_clone2 = Arc::clone(write_tx_am);
+        // tokio::spawn(async move {
+        //     trace!("线程-异步心跳-开始");
+        //     let ping_str = json!({
+        //         "method": "server.ping",
+        //         "params": {},
+        //         "id": 1
+        //     });
+        //     AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Custom(ping_str.to_string()), heartbeat_time).await;
+        //     trace!("线程-异步心跳-结束");
+        // });
+
+        //设置订阅
+        let mut subscribe_array = vec![];
+
+
+        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));
+
+            info!("启动连接");
+            loop {
+                info!("htx_usdt_swap socket 连接中……");
+                // 需要登录
+                if login_is {
+                    let mut login_data = LOGIN_DATA.lock().await;
+                    let login_param_real = login_param.clone().unwrap();
+                    login_data.0 = true;
+                    let utc_now = Utc::now();
+                    let timestamp = utc_now.format("%Y-%m-%dT%H:%M:%S").to_string();
+                    let timestamp_str = percent_encoding::utf8_percent_encode(timestamp.clone().as_str(), crate::htx_swap_rest::FRAGMENT).to_string();
+
+                    let access_key = login_param_real.api_key.clone();
+                    let secret_key = login_param_real.secret.clone();
+                    let param_str = format!("AccessKeyId={}&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp={}", access_key, timestamp_str);
+
+                    let signature = {
+                        let message = format!("GET\napi.hbdm.vn\n/linear-swap-notification\n{}", param_str);
+                        trace!("组装数据:\n{}", 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
+                    };
+                    let login_param = json!({
+                        "op": "auth",
+                        "type": "api",
+                        "AccessKeyId": access_key,
+                        "SignatureMethod": "HmacSHA256",
+                        "SignatureVersion": "2",
+                        "Timestamp": timestamp,
+                        "Signature": signature,
+                    });
+                    let login_str = login_param.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;
+                }
+
+                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_sync, Self::message_ping, Self::message_pong, Self::message_binary_sync,
+                ).await;
+                let mut login_data = LOGIN_DATA.lock().await;
+                // 断联后 设置为没有登录
+                login_data.1 = false;
+                info!("htx_usdt_swap socket 断连,1s以后重连……");
+                error!("htx_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub async fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text).await;
+        Option::from(response_data)
+    }
+    pub fn message_text_sync(text: String) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_text(text))
+        })
+    }
+    //数据解析-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 async fn message_binary(binary: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = Self::parse_zip_data(binary);
+        let response_data = Self::ok_text(message_str).await;
+        Option::from(response_data)
+    }
+    pub fn message_binary_sync(binary: Vec<u8>) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_binary(binary))
+        })
+    }
+    //数据解析
+    pub async 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();
+
+        /*公共:响应*/
+        //心跳包
+        let ping = json_value["ping"].as_i64();
+        match ping {
+            Some(ping_ts) => {
+                let pong = json!({
+                    "pong": ping_ts
+                });
+                return ResponseData::new("".to_string(), -302, "success".to_string(), pong);
+            }
+            None => {}
+        }
+        //推送数据
+        let status = json_value["status"].as_str();
+        match status {
+            Some(v) => {
+                match v {
+                    "ok" => {
+                        res_data.channel = format!("{}", v);
+                        res_data.code = -201;
+                        res_data.data = json_value["data"].clone();
+                    }
+                    "error" => {
+                        res_data.code = 400;
+                        res_data.message = format!("{}", json_value["err-msg"].as_str().unwrap());
+                        res_data.channel = format!("{}", json_value["id"].as_str().unwrap());
+                    }
+                    &_ => {}
+                }
+                return res_data;
+            }
+            None => {}
+        }
+        let ch = json_value["ch"].as_str();
+        match ch {
+            Some(channel) => {
+                res_data.channel = channel.parse().unwrap();
+                res_data.code = 200;
+                res_data.data = json_value["tick"].clone();
+                return res_data;
+            }
+            None => {}
+        }
+
+
+        /*私有:响应*/
+        let op = json_value["op"].as_str().unwrap();
+        match op {
+            "auth" => {
+                let op = json_value["err-code"].as_i64().unwrap();
+                res_data.channel = "auth".to_string();
+                if op == 0 {
+                    res_data.code = -200;
+                    res_data.message = "登录成功".to_string();
+                    res_data.data = json_value["data"].clone();
+                } else {
+                    res_data.code = 400;
+                    res_data.message = format!("登录失败:{}", json_value["err-msg"].as_str().unwrap());
+                }
+                return res_data;
+            }
+            "ping" => {
+                let ts = json_value["ts"].as_str().unwrap();
+                let pong = json!({
+                    "op": "pong",
+                    "ts": ts
+                });
+                return ResponseData::new("".to_string(), -302, "success".to_string(), pong);
+            }
+            "sub" => {
+                res_data.channel = json_value["topic"].as_str().unwrap().to_string();
+                res_data.code = -201;
+                res_data.message = "订阅成功".to_string();
+                return res_data;
+            }
+            "notify" => {
+                res_data.channel = json_value["topic"].as_str().unwrap().to_string();
+                res_data.code = 200;
+                res_data.message = "推送数据".to_string();
+                if json_value.get("data").is_some() {
+                    res_data.data = json_value["data"].clone();
+                } else {
+                    res_data.data = json_value.clone();
+                }
+
+                return res_data;
+            }
+            _ => {}
+        }
+
+
+        res_data.code = 400;
+        res_data.message = format!("未知响应内容");
+        res_data.data = text.parse().unwrap();
+        trace!("--------------------------------");
+        res_data
+    }
+
+    fn parse_zip_data(p0: Vec<u8>) -> String {
+        // 创建一个GzDecoder的实例,将压缩数据作为输入
+        let mut decoder = GzDecoder::new(&p0[..]);
+
+        // 创建一个缓冲区来存放解压缩后的数据
+        let mut decompressed_data = Vec::new();
+
+        // 读取解压缩的数据到缓冲区中
+        decoder.read_to_end(&mut decompressed_data).expect("解压缩失败");
+        let result = from_utf8(&decompressed_data)
+            .expect("解压缩后的数据不是有效的UTF-8");
+
+        // info!("解压缩数据 {:?}", result);
+        result.to_string()
+    }
+}
+

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

+ 403 - 0
exchanges/src/kucoin_swap_ws.rs

@@ -0,0 +1,403 @@
+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, Self::message_binary).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 message_binary(_po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = format!("Binary:{:?}", _po);
+        Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData
+    {
+        // trace!("原始数据:{:?}",text);
+        let mut res_data = ResponseData::new("".to_string(), 200, "success".to_string(), Value::Null);
+        let json_value: 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
+    }
+}

+ 44 - 0
exchanges/src/lib.rs

@@ -0,0 +1,44 @@
+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 bybit_swap_ws;
+pub mod coinex_swap_ws;
+pub mod coinex_swap_rest;
+pub mod htx_swap_ws;
+pub mod htx_swap_rest;
+pub mod bingx_swap_ws;
+pub mod bingx_swap_rest;
+pub mod mexc_swap_ws;
+pub mod mexc_swap_rest;
+pub mod bitmart_swap_rest;
+pub mod bitmart_swap_ws;
+pub mod coinsph_swap_rest;
+pub mod coinsph_swap_ws;
+pub mod phemex_swap_rest;
+pub mod phemex_swap_ws;
+pub mod woo_swap_ws;
+pub mod woo_swap_rest;
+pub mod cointr_swap_rest;
+pub mod cointr_swap_ws;

+ 314 - 0
exchanges/src/mexc_swap_rest.rs

@@ -0,0 +1,314 @@
+use std::collections::BTreeMap;
+use chrono::Utc;
+use reqwest::header::HeaderMap;
+use reqwest::{Client};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::{error, info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use serde_json::Value;
+
+#[derive(Clone, Debug)]
+pub struct MexcSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl MexcSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> MexcSwapRest
+    {
+        return MexcSwapRest::new_with_tag("default-MexcSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> MexcSwapRest {
+        let base_url = if is_colo {
+            "https://contract.mexc.com".to_string()
+        } else {
+            "https://contract.mexc.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        MexcSwapRest {
+            tag,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //获取合约信息
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                "/contract/detail".to_string(),
+                                false,
+                                params,
+        ).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,
+                         mut  params: Value) -> 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 timestamp = Utc::now().timestamp_millis();
+        let recv_window = 3000;
+        params["timestamp"] = serde_json::json!(timestamp);
+        params["recvWindow"] = serde_json::json!(recv_window);
+
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value);
+
+        let code = json_value["code"].as_i64().unwrap();
+        match code {
+            0 => {
+                //判断是否有code ,没有表示特殊接口,直接返回
+                if json_value.get("data").is_some() {
+                    let data = json_value.get("data").unwrap();
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), data.clone())
+                } else {
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value)
+                }
+            }
+            _ => {
+                ResponseData::new(self.tag.clone(), 400, "error".to_string(), json_value)
+            }
+        }
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 368 - 0
exchanges/src/mexc_swap_ws.rs

@@ -0,0 +1,368 @@
+use std::io::Read;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use flate2::read::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::json;
+use serde_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 MexcSwapWsType {
+    PublicAndPrivate,
+}
+
+
+#[derive(Debug)]
+#[derive(Clone)]
+pub struct MexcSwapWsParam {
+    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 MexcSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+}
+
+//账号信息
+#[derive(Clone, Debug)]
+pub struct MexcSwapLogin {
+    pub access_key: String,
+    pub secret_key: String,
+    pub pass_key: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct MexcSwapWs {
+    //类型
+    tag: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<MexcSwapLogin>,
+    //登录数据
+    ws_param: MexcSwapWsParam,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<MexcSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl MexcSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<MexcSwapLogin>, ws_type: MexcSwapWsType) -> MexcSwapWs {
+        return Self::new_with_tag("default-MexcSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: Option<MexcSwapLogin>, ws_type: MexcSwapWsType) -> MexcSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            MexcSwapWsType::PublicAndPrivate => {
+                let url = "wss://contract.mexc.com/edge".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        /*******公共频道-私有频道数据组装*/
+        let ws_param = MexcSwapWsParam {
+            token: "".to_string(),
+            ws_url: "".to_string(),
+            ws_ping_interval: 0,
+            ws_ping_timeout: 0,
+            is_ok_subscribe: false,
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        MexcSwapWs {
+            tag,
+            address_url,
+            login_param,
+            ws_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 18,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<MexcSwapSubscribeType>) {
+        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 {
+                MexcSwapSubscribeType::PuFuturesTrades => false,
+                MexcSwapSubscribeType::PuFuturesRecords => false,
+                MexcSwapSubscribeType::PuFuturesDepth => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: MexcSwapSubscribeType) -> Value {
+        match subscribe_type {
+            MexcSwapSubscribeType::PuFuturesDepth => {//深度
+                json!({
+                   "method":"sub.depth",
+                   "param":{ "symbol":symbol }
+                })
+            }
+            MexcSwapSubscribeType::PuFuturesRecords => {//k线
+                json!({
+                    "method":"sub.kline",
+                    "param":{
+                        "symbol":symbol,
+                        "interval":"Min1"
+                    }
+                })
+            }
+            MexcSwapSubscribeType::PuFuturesTrades => {//公开成交
+                json!({
+                    "method": "sub.deal",
+                    "param": {"symbol":symbol}
+                })
+            }
+        }
+    }
+    //订阅信息生成
+    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 tag = self.tag.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!("Mexc_usdt_swap socket 连接中……");
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, tag.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong, Self::message_binary).await;
+
+                error!("Mexc_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text);
+        Option::from(response_data)
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -300, "success".to_string(), Value::Null));
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null));
+    }
+    //数据解析-二进制
+    pub fn message_binary(po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        // let message_str = format!("Binary:{:?}", _po);
+        // Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+        // let result = String::from_utf8(bytes);
+        // let result = String::from_utf8(po);
+
+        let mut gz_decoder = GzDecoder::new(&po[..]);
+        let mut decompressed_data = Vec::new();
+
+        // 尝试解压数据
+        if let Ok(_) = gz_decoder.read_to_end(&mut decompressed_data) {
+            // 将解压后的字节向量转换为 UTF-8 字符串
+            match String::from_utf8(decompressed_data) {
+                Ok(text) => {
+                    let response_data = Self::ok_text(text);
+                    return Option::from(response_data);
+                }
+                Err(_) => {
+                    return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".to_string(), Value::Null));
+                }
+            }
+        } else {
+            return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".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();
+
+        match json_value["channel"].as_str() {
+            Some(method) => {
+                if method.contains("pong") {
+                    return ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null);
+                } else if method.contains("rs.sub.") {
+                    //订阅响应
+                    let data = json_value["data"].as_str().unwrap();
+                    if method.contains(".depth") {
+                        res_data.channel = "futures.order_book".to_string();
+                    } else if method.contains(".kline") {
+                        res_data.channel = "futures.candlesticks".to_string();
+                    } else if method.contains(".deal") {
+                        res_data.channel = "futures.trades".to_string();
+                    } else {
+                        res_data.channel = "未知频道订阅".to_string();
+                    }
+
+                    if data == "success" {
+                        res_data.code = -201;
+                        res_data.message = "订阅成功".to_string();
+                    } else {
+                        res_data.code = 400;
+                        res_data.message = "订阅失败".to_string();
+                    }
+                } else if method.contains("push.") {
+                    if method.contains(".depth") {
+                        res_data.channel = "futures.order_book".to_string();
+                    } else if method.contains(".kline") {
+                        res_data.channel = "futures.candlesticks".to_string();
+                    } else if method.contains(".deal") {
+                        res_data.channel = "futures.trades".to_string();
+                    } else {
+                        res_data.channel = "未知频道推送".to_string();
+                    }
+                    res_data.code = 200;
+                    res_data.data = json_value.clone();
+                } else {
+                    res_data.code = -1;
+                    res_data.message = "未知解析".to_string();
+                }
+            }
+            None => {
+                res_data.code = -1;
+                res_data.message = "未知解析".to_string();
+            }
+        }
+        //
+        // if json_value["method"].as_str() == Option::from("id1") {}
+        //
+        // // { "id": "id1", "code": 0, "msg": "" }
+        // if json_value["id"].as_str() == Option::from("id1") {
+        //     //订阅
+        //     if json_value["code"].as_i64() == Option::from(0) {
+        //         res_data.code = -201;
+        //         res_data.message = "订阅成功".to_string();
+        //     } else {
+        //         res_data.code = 400;
+        //         res_data.message = "订阅失败".to_string();
+        //     }
+        // } else if json_value["code"].as_i64() == Option::from(0) {
+        //     res_data.code = 200;
+        //     res_data.data = json_value.clone();
+        //
+        //     //订阅数据 甄别
+        //     let dataType = json_value["dataType"].as_str().unwrap();
+        //     if dataType.contains("@depth") {
+        //         res_data.channel = "futures.order_book".to_string();
+        //     } else if dataType.contains("@trade") {
+        //         res_data.channel = "futures.trades".to_string();
+        //     } else if dataType.contains("@kline_1m") {
+        //         res_data.channel = "futures.candlesticks".to_string();
+        //     } else {
+        //         res_data.channel = "未知推送数据".to_string();
+        //     }
+        // } else {
+        //     res_data.code = -1;
+        //     res_data.message = "未知解析".to_string();
+        // }
+
+        res_data
+    }
+}

+ 305 - 0
exchanges/src/okx_swap_rest.rs

@@ -0,0 +1,305 @@
+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::{error, info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use serde_json::Value;
+
+#[derive(Clone, Debug)]
+pub struct OkxSwapRest {
+    pub tag: 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_with_tag("default-OkxSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: 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 {
+            tag,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //获取合约信息
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/public/instruments".to_string(),
+                                false,
+                                params,
+        ).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: Value) -> 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
+        }
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value)
+        let code = json_value["code"].as_str().unwrap();
+        match code {
+            "0" => {
+                //判断是否有code ,没有表示特殊接口,直接返回
+                if json_value.get("data").is_some() {
+                    let data = json_value.get("data").unwrap();
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), data.clone())
+                } else {
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value)
+                }
+            }
+            _ => {
+                ResponseData::new(self.tag.clone(), 400, "error".to_string(), json_value)
+            }
+        }
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

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

+ 314 - 0
exchanges/src/phemex_swap_rest.rs

@@ -0,0 +1,314 @@
+use std::collections::BTreeMap;
+use chrono::Utc;
+use reqwest::header::HeaderMap;
+use reqwest::{Client};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::{error, info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use serde_json::Value;
+
+#[derive(Clone, Debug)]
+pub struct PhemexSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl PhemexSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> PhemexSwapRest
+    {
+        return PhemexSwapRest::new_with_tag("default-PhemexSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> PhemexSwapRest {
+        let base_url = if is_colo {
+            "https://api.phemex.com".to_string()
+        } else {
+            "https://api.phemex.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        PhemexSwapRest {
+            tag,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //查詢合約基礎信息
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/public/products".to_string(),
+                                false,
+                                params,
+        ).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,
+                         mut  params: Value) -> 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 timestamp = Utc::now().timestamp_millis();
+        let recv_window = 3000;
+        params["timestamp"] = serde_json::json!(timestamp);
+        params["recvWindow"] = serde_json::json!(recv_window);
+
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/x-www-form-urlencoded".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value);
+
+        let code = json_value["code"].as_i64().unwrap();
+        match code {
+            0 => {
+                //判断是否有code ,没有表示特殊接口,直接返回
+                if json_value.get("data").is_some() {
+                    let data = json_value.get("data").unwrap();
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), data.clone())
+                } else {
+                    ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value)
+                }
+            }
+            _ => {
+                ResponseData::new(self.tag.clone(), 400, "error".to_string(), json_value)
+            }
+        }
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 339 - 0
exchanges/src/phemex_swap_ws.rs

@@ -0,0 +1,339 @@
+use std::io::Read;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use flate2::read::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::json;
+use serde_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, HeartbeatType};
+
+//类型
+pub enum PhemexSwapWsType {
+    PublicAndPrivate,
+}
+
+
+#[derive(Debug)]
+#[derive(Clone)]
+pub struct PhemexSwapWsParam {
+    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 PhemexSwapSubscribeType {
+    // 订单
+    PuFuturesOrderBook,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+}
+
+//账号信息
+#[derive(Clone, Debug)]
+pub struct PhemexSwapLogin {
+    pub access_key: String,
+    pub secret_key: String,
+    pub pass_key: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct PhemexSwapWs {
+    //类型
+    tag: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<PhemexSwapLogin>,
+    //登录数据
+    ws_param: PhemexSwapWsParam,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<PhemexSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl PhemexSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<PhemexSwapLogin>, ws_type: PhemexSwapWsType) -> PhemexSwapWs {
+        return Self::new_with_tag("default-PhemexSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: Option<PhemexSwapLogin>, ws_type: PhemexSwapWsType) -> PhemexSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            PhemexSwapWsType::PublicAndPrivate => {
+                let url = "wss://ws.phemex.com".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        /*******公共频道-私有频道数据组装*/
+        let ws_param = PhemexSwapWsParam {
+            token: "".to_string(),
+            ws_url: "".to_string(),
+            ws_ping_interval: 15 * 1000,
+            ws_ping_timeout: 0,
+            is_ok_subscribe: false,
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        PhemexSwapWs {
+            tag,
+            address_url,
+            login_param,
+            ws_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 18,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<PhemexSwapSubscribeType>) {
+        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_string();
+            // 字符串替换
+            *symbol = symbol.replace("_", "-");
+        }
+        self.symbol_s = b_array;
+    }
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                PhemexSwapSubscribeType::PuFuturesTrades => false,
+                PhemexSwapSubscribeType::PuFuturesRecords => false,
+                PhemexSwapSubscribeType::PuFuturesOrderBook => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: PhemexSwapSubscribeType) -> Value {
+        match subscribe_type {
+            PhemexSwapSubscribeType::PuFuturesOrderBook => {
+                // format!("{}@depth5@100ms", symbol)
+                json!({
+                    "id": 999,
+                    "method": "orderbook_p.subscribe",
+                    "params": [
+                        symbol,
+                    ]
+                })
+            }
+            PhemexSwapSubscribeType::PuFuturesRecords => {
+                // format!("{}@kline_1m", symbol)
+                json!({
+                    "id": 999,
+                    "method": "kline_p.subscribe",
+                    "params": [
+                        symbol,
+                        60
+                    ]
+                })
+            }
+            PhemexSwapSubscribeType::PuFuturesTrades => {
+                json!({
+                    "id": 999,
+                    "method": "trade_p.subscribe",
+                    "params": [
+                        symbol
+                    ]
+                })
+            }
+        }
+    }
+    //订阅信息生成
+    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 tag = self.tag.clone();
+        let heartbeat_time = self.ws_param.ws_ping_interval.clone();
+
+        // 心跳-- 方法内部线程启动
+        let write_tx_clone1 = write_tx_am.clone();
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            let info = json!({
+                "id": 0,
+                "method": "server.ping",
+                "params": []
+            });
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Custom(info.to_string()), 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!("Phemex_usdt_swap socket 连接中……");
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, tag.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong, Self::message_binary).await;
+
+                error!("Phemex_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text);
+        Option::from(response_data)
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -300, "success".to_string(), Value::Null));
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null));
+    }
+    //数据解析-二进制
+    pub fn message_binary(po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        // let message_str = format!("Binary:{:?}", _po);
+        // Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+        // let result = String::from_utf8(bytes);
+        // let result = String::from_utf8(po);
+
+        let mut gz_decoder = GzDecoder::new(&po[..]);
+        let mut decompressed_data = Vec::new();
+
+        // 尝试解压数据
+        if let Ok(_) = gz_decoder.read_to_end(&mut decompressed_data) {
+            // 将解压后的字节向量转换为 UTF-8 字符串
+            match String::from_utf8(decompressed_data) {
+                Ok(text) => {
+                    let response_data = Self::ok_text(text);
+                    return Option::from(response_data);
+                }
+                Err(_) => {
+                    return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".to_string(), Value::Null));
+                }
+            }
+        } else {
+            return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".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();
+
+        // { "id": "id1", "code": 0, "msg": "" }
+        if json_value["id"].as_i64() == Option::from(0) {
+            //订阅
+            if json_value["result"].as_str() == Option::from("pong") {
+                res_data.code = -301;
+                res_data.message = "Pong".to_string();
+            }
+        } else if json_value["id"].as_i64() == Option::from(999) {
+            //订阅
+            if json_value["result"]["status"].as_str() == Option::from("success") {
+                res_data.code = -201;
+                res_data.message = "订阅成功".to_string();
+            } else {
+                res_data.code = 400;
+                res_data.message = "订阅失败".to_string();
+            }
+        } else if json_value["type"].as_str() == Option::from("incremental") {
+            if !json_value["kline_p"].is_null() {
+                res_data.code = 200;
+                res_data.data = json_value.clone();
+                res_data.channel = "futures.candlesticks".to_string();
+            } else if !json_value["trades_p"].is_null() {
+                res_data.code = 200;
+                res_data.data = json_value.clone();
+                res_data.channel = "futures.trades".to_string();
+            } else if !json_value["orderbook_p"].is_null() {
+                res_data.code = 200;
+                res_data.data = json_value.clone();
+                res_data.channel = "futures.order_book".to_string();
+            }
+        } else {
+            res_data.code = -1;
+            res_data.message = "未知解析".to_string();
+        }
+
+        res_data
+    }
+}

+ 134 - 0
exchanges/src/proxy.rs

@@ -0,0 +1,134 @@
+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(is_unusual: Option<&str>) -> ParsingDetail {
+        let proxy_address_name = match is_unusual {
+            None => {
+                "proxy_address"
+            }
+            Some(v) => {
+                match v {
+                    "binance" => {
+                        "binance_proxy_address"
+                    }
+                    _ => {
+                        "proxy_address"
+                    }
+                }
+            }
+        };
+        let proxy_address = env::var(proxy_address_name);
+        // 使用std::env::var函数获取环境变量的值,如果返回Err,则说明环境变量不存在
+        match proxy_address {
+            Ok(value) => {
+                trace!("环境变量读取成功:key:proxy_address , val:{}", value);
+                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(is_unusual: Option<&str>) -> bool {
+        //拿到环境变量解析的数据
+        let parsing_detail = Self::parsing_environment_variables(is_unusual);
+        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
+        }
+    }
+
+    pub fn removes_proxy(is_unusual: Option<&str>) -> bool {
+        //拿到环境变量解析的数据
+        let parsing_detail = Self::parsing_environment_variables(is_unusual);
+        if parsing_detail.ip_address.len() > 0 && parsing_detail.port.len() > 0 {
+            env::remove_var("http_proxy");
+            env::remove_var("https_proxy");
+            true
+        } else {
+            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)
+    }
+}
+

+ 457 - 0
exchanges/src/socket_tool.rs

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

+ 9 - 0
exchanges/src/utils.rs

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

+ 305 - 0
exchanges/src/woo_swap_rest.rs

@@ -0,0 +1,305 @@
+use std::collections::BTreeMap;
+// use chrono::Utc;
+use reqwest::header::HeaderMap;
+use reqwest::{Client};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::{error, info, trace};
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use ring::hmac;
+use serde_json::{json, Value};
+
+#[derive(Clone, Debug)]
+pub struct WooSwapRest {
+    pub tag: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl WooSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> WooSwapRest
+    {
+        return WooSwapRest::new_with_tag("default-WooSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> WooSwapRest {
+        let base_url = if is_colo {
+            "https://api.woo.org".to_string()
+        } else {
+            "https://api.woo.org".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        WooSwapRest {
+            tag,
+            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 = json!({});
+        let data = self.request("GET".to_string(),
+                                "/v1".to_string(),
+                                "/public/system_info".to_string(),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+    //获取合约信息(获取所有市场的期货信息(公开))
+    pub async fn get_market(&mut self,params:Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "/v1".to_string(),
+                                "/public/info".to_string(),
+                                false,
+                                params,
+        ).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: Value) -> 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 timestamp = Utc::now().timestamp_millis();
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "{}".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+            body = params.to_string();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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 response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.to_string(),
+            body,
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+        //
+        // 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.to_string());
+        // res_data
+    }
+
+    // pub fn headers(_: 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> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: 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()))
+        };
+
+        trace!("url-----:???{}",url.clone());
+        trace!("addrs_url-----:???{}",addrs_url.clone());
+        trace!("params-----:???{}",params.clone());
+        trace!("body-----:???{}",body.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(body).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();
+        // trace!("text:???{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        return ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value.clone());
+    }
+
+    pub fn on_error_data(&mut self, text: &String, base_url: &String, params: &String) -> ResponseData {
+        let json_value = serde_json::from_str::<Value>(&text);
+
+        match json_value {
+            Ok(data) => {
+                let message;
+
+                if !data["message"].is_null() {
+                    message = format!("{}:{}", data["tag"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["tag"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 411 - 0
exchanges/src/woo_swap_ws.rs

@@ -0,0 +1,411 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use chrono::Utc;
+use flate2::bufread::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use once_cell::sync::Lazy;
+use ring::hmac;
+use serde_json::{json, Value};
+use tokio::sync::Mutex;
+use tokio::task;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode};
+// use crate::socket_tool::{AbstractWsMode, HeartbeatType};
+
+pub(crate) static LOGIN_DATA: Lazy<Mutex<(bool, bool)>> = Lazy::new(|| {
+    println!("初始化...");
+    // 0: 需要登录, 1:是否已经登录
+    Mutex::new((false, false))
+});
+
+
+pub enum WooSwapWsType {
+    Public(String),
+    Private(String),
+}
+
+
+//订阅频道
+#[derive(Clone)]
+pub enum WooSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+
+    // // 深度
+    // PuFuturesDepth,
+    // // 公开成交
+    // PuFuturesTrades,
+    // // K线数据
+    // PuFuturesRecords,
+    //
+    // // 订单
+    // PrFuturesOrders,
+    // // 仓位
+    // PrFuturesPositions,
+    // // 余额
+    // PrFuturesBalances,
+}
+
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct WooSwapLogin {
+    pub api_key: String,
+    pub secret: String,
+    pub api_memo: String,
+}
+
+#[derive(Clone)]
+pub struct WooSwapWs {
+    tag: String,
+    // 类型
+    address_url: String,
+    // 地址
+    login_param: Option<WooSwapLogin>,
+    // 账号
+    symbol_s: Vec<String>,
+    // 币对
+    subscribe_types: Vec<WooSwapSubscribeType>,
+    // 订阅
+    _heartbeat_time: u64,                                        // 心跳间隔
+}
+
+
+impl WooSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************实例化一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<WooSwapLogin>, ws_type: WooSwapWsType) -> WooSwapWs {
+        return Self::new_with_tag("default-BingxSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+
+    pub fn new_with_tag(tag: String, _is_colo: bool, login_param: Option<WooSwapLogin>, ws_type: WooSwapWsType) -> WooSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            WooSwapWsType::Public(v) => {
+                let url = format!("wss://wss.staging.woo.org/ws/stream/{}", v);
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+            WooSwapWsType::Private(v) => {
+                let url = format!("wss://wss.staging.woo.org/ws/stream/{}", v);
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        WooSwapWs {
+            tag,
+            address_url,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            _heartbeat_time: 1000 * 5,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<WooSwapSubscribeType>) {
+        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 = format!("PERP_{}", 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 {
+                WooSwapSubscribeType::PuFuturesDepth => false,
+                WooSwapSubscribeType::PuFuturesTrades => false,
+                WooSwapSubscribeType::PuFuturesRecords => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: WooSwapSubscribeType, _login_param: Option<WooSwapLogin>) -> String {
+        match subscribe_type {
+            WooSwapSubscribeType::PuFuturesDepth => {//深度
+                json!( {
+                    "id": "clientIDsd01",
+                    "topic": format!("{}@orderbook",symbol),
+                    "event": "subscribe"
+                }).to_string()
+            }
+            WooSwapSubscribeType::PuFuturesTrades => {//公开成交
+                json!( {
+                    "id": "clientIDsd01",
+                    "topic": format!("{}@trades",symbol),
+                    "event": "subscribe"
+                }).to_string()
+            }
+            WooSwapSubscribeType::PuFuturesRecords => {//k线数据
+                json!( {
+                    "id": "clientIDsd01",
+                    "topic": format!("{}@kline_1m",symbol),
+                    "event": "subscribe"
+                }).to_string()
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<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(),
+                                                  self.login_param.clone(),
+                );
+                args.push(ty_str);
+            }
+        }
+        return args;
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************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 login_param = self.login_param.clone();
+        let subscription = self.get_subscription();
+        let address_url = self.address_url.clone();
+        let label = self.tag.clone();
+        // let heartbeat_time = self.heartbeat_time.clone();
+
+
+        //心跳-- 方法内部线程启动
+        // let write_tx_clone1 = Arc::clone(write_tx_am);
+        let write_tx_clone2 = 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![];
+        for su in subscription {
+            subscribe_array.push(su);
+        }
+
+
+        //链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            info!("启动连接");
+            loop {
+                info!("Woo_usdt_swap socket 连接中……");
+                // 需要登录
+                if login_is {
+                    let mut login_data = LOGIN_DATA.lock().await;
+                    let login_param_real = login_param.clone().unwrap();
+                    login_data.0 = true;
+
+                    let timestamp = Utc::now().timestamp_millis().to_string();
+                    let api_key = login_param_real.api_key.clone();
+                    let secret_key = login_param_real.secret.clone();
+                    let api_memo = login_param_real.api_memo.clone();
+
+
+                    // let timestamp = "1589267764859".to_string();
+                    // let api_key = "80618e45710812162b04892c7ee5ead4a3cc3e56".to_string();
+                    // let secret_key = "6c6c98544461bbe71db2bca4c6d7fd0021e0ba9efc215f9c6ad41852df9d9df9".to_string();
+                    // let api_memo = "test001".to_string();
+
+                    let sign = {
+                        let message = format!("{}#{}#Woo.WebSocket", timestamp.clone(), api_memo);
+                        trace!("组装数据:\n{}", message);
+
+                        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
+                    };
+
+
+                    let mut args = vec![];
+                    args.push(api_key.clone());
+                    args.push(timestamp.clone());
+                    args.push(sign.clone());
+                    args.push(String::from("web"));
+                    // {"action":"access","args":["<API_KEY>","<timestamp>","<sign>","<dev>"]}
+                    let login_param = json!({
+                        "action": "access",
+                        "args": [
+                           api_key, timestamp.as_str(),sign.as_str(),"web"
+                        ]
+                    });
+                    let login_str = login_param.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;
+                }
+
+                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_sync, Self::message_ping, Self::message_pong, Self::message_binary_sync,
+                ).await;
+                let mut login_data = LOGIN_DATA.lock().await;
+                // 断联后 设置为没有登录
+                login_data.1 = false;
+                info!("Woo_usdt_swap socket 断连,1s以后重连……");
+                error!("Woo_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub async fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text).await;
+        Option::from(response_data)
+    }
+    pub fn message_text_sync(text: String) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_text(text))
+        })
+    }
+    //数据解析-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 async fn message_binary(binary: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        let message_str = Self::parse_zip_data(binary);
+        let response_data = Self::ok_text(message_str).await;
+        Option::from(response_data)
+    }
+    pub fn message_binary_sync(binary: Vec<u8>) -> Option<ResponseData> {
+        // 使用 tokio::task::block_in_place 来等待异步函数的结果
+        task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(Self::message_binary(binary))
+        })
+    }
+    //数据解析
+    pub async fn ok_text(text: String) -> ResponseData
+    {
+        // info!("原始数据:{}", 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();
+
+        let event = json_value["event"].as_str();
+        match event {
+            None => {}
+            Some(v) => {
+                match v {
+                    "subscribe" => {
+                        let success = json_value["success"].as_bool().unwrap();
+                        if success == true {
+                            res_data.code = -201;
+                            res_data.message = format!("订阅成功:{}", json_value["data"].clone().to_string());
+                            return res_data;
+                        } else {
+                            res_data.code = 400;
+                            res_data.message = format!("订阅失败:{}", json_value["errorMsg"].clone().to_string());
+                            return res_data;
+                        }
+                    }
+                    "ping" => {
+                        let data = json!({   "event":"ping"  });
+                        return ResponseData::new("".to_string(), -300, "success".to_string(), Value::String(data.to_string()));
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        let topic = json_value["topic"].as_str();
+        match topic {
+            None => {}
+            Some(v) => {
+                res_data.code = 200;
+                res_data.data = json_value.clone();
+                if v.contains("orderbook") {
+                    res_data.channel = "futures.order_book".to_string();
+                } else if v.contains("trades") {
+                    res_data.channel = "futures.trades".to_string();
+                } else if v.contains("kline") {
+                    res_data.channel = "futures.candlesticks".to_string();
+                } else {
+                    res_data.code = 400;
+                    res_data.channel = "未知推送数据".to_string();
+                }
+                return res_data;
+            }
+        }
+
+        res_data.code = 400;
+        res_data.message = format!("未知响应内容");
+        res_data.data = text.parse().unwrap();
+        trace!("--------------------------------");
+        res_data
+    }
+
+    fn parse_zip_data(p0: Vec<u8>) -> String {
+        // 创建一个GzDecoder的实例,将压缩数据作为输入
+        let mut decoder = GzDecoder::new(&p0[..]);
+
+        // 创建一个缓冲区来存放解压缩后的数据
+        let mut decompressed_data = Vec::new();
+
+        // 读取解压缩的数据到缓冲区中
+        decoder.read_to_end(&mut decompressed_data).expect("解压缩失败");
+        let result = from_utf8(&decompressed_data)
+            .expect("解压缩后的数据不是有效的UTF-8");
+
+        // info!("解压缩数据 {:?}", result);
+        result.to_string()
+    }
+}
+

+ 92 - 0
exchanges/src/xlsx_utils.rs

@@ -0,0 +1,92 @@
+use std::collections::BTreeMap;
+use rust_xlsxwriter::*;
+
+pub fn creation_xlsx(one_row_name: &Vec<&str>, data_rows: &BTreeMap<String, Vec<Vec<String>>>, file_name: String) -> Result<(), XlsxError> {
+
+    // 创建一个新的Excel文件对象。
+    let mut workbook = Workbook::new();
+    for (key, value) in data_rows {
+        // 向工作簿中添加工作表。
+        let  worksheet = workbook.add_worksheet();
+        worksheet.set_name(key).unwrap();
+
+        // 第一行 列,明
+        let mut row_index: usize = 0;
+        let mut i: usize = 0;
+        let bold_format = Format::new().set_bold();
+        while i <= one_row_name.len() - 1 {
+            worksheet.write_with_format(row_index as RowNum, i as ColNum, one_row_name[i], &bold_format)?;
+            i = i + 1;
+        }
+
+
+        //后续 数据写入
+        row_index = 1;
+        while row_index <= value.len()  {
+            let row = value.get(row_index - 1).unwrap();
+
+            i = 0;
+            for str in row {
+                worksheet.write(row_index as RowNum, i as ColNum, str)?;
+                i = i + 1;
+            }
+            row_index = row_index + 1;
+        }
+    }
+
+
+    //
+    // for row in one_row_name {}
+    // // 写一个不带格式的字符串。
+    // worksheet.write(0, 0, "Hello")?;
+    //
+    //
+    // // 创建一些要在工作表中使用的格式。
+    // let bold_format = Format::new().set_bold();
+    // let decimal_format = Format::new().set_num_format("0.000");
+    // let date_format = Format::new().set_num_format("yyyy-mm-dd");
+    // let merge_format = Format::new()
+    //     .set_border(FormatBorder::Thin)
+    //     .set_align(FormatAlign::Center);
+    //
+    //
+    // // 为清晰设置列宽度。
+    // worksheet.set_column_width(0, 22)?;
+    //
+    // // 写一个不带格式的字符串。
+    // worksheet.write(0, 0, "Hello")?;
+    //
+    // // 用上面定义的粗体格式编写一个字符串。
+    // worksheet.write_with_format(1, 0, "World", &bold_format)?;
+    //
+    // //
+    // worksheet.write(2, 0, 1)?;
+    // worksheet.write(3, 0, 2.34)?;
+    //
+    // // 用格式写一个数字。
+    // worksheet.write_with_format(4, 0, 3.00, &decimal_format)?;
+    //
+    // // 写一个公式。
+    // worksheet.write(5, 0, Formula::new("=SIN(PI()/4)"))?;
+    //
+    // // 写一个日期。
+    // let date = ExcelDateTime::from_ymd(2023, 1, 25)?;
+    // worksheet.write_with_format(6, 0, &date, &date_format)?;
+    //
+    // //写一些链接。
+    // worksheet.write(7, 0, Url::new("https://www.rust-lang.org"))?;
+    // worksheet.write(8, 0, Url::new("https://www.rust-lang.org").set_text("Rust"))?;
+
+    // 写入一些合并的单元格。
+    // worksheet.merge_range(9, 0, 9, 1, "Merged cells", &merge_format)?;
+
+    // Insert an image.
+    // let image = Image::new("examples/rust_logo.png")?;
+    // worksheet.insert_image(1, 2, &image)?;
+
+    // 生成文件
+    let path = format!("./{}.xlsx", file_name);
+    workbook.save(path)?;
+
+    Ok(())
+}

+ 5 - 0
global/.gitignore

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

+ 28 - 0
global/Cargo.toml

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

+ 69 - 0
global/src/account_info.rs

@@ -0,0 +1,69 @@
+use std::fs::File;
+use std::io::Read;
+use serde_derive::Deserialize;
+use toml::from_str;
+use tracing::{error};
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct AccountInfo {
+    pub gate_access_key: String,
+    pub gate_secret_key: String,
+    pub binance_access_key: String,
+    pub binance_secret_key: String,
+    pub kucoin_access_key: String,
+    pub kucoin_secret_key: String,
+    pub kucoin_pass: String,
+    pub okx_access_key: String,
+    pub okx_secret_key: String,
+    pub okx_pass: String,
+    pub bitget_access_key: String,
+    pub bitget_secret_key: String,
+    pub bitget_pass: String,
+    pub bybit_access_key: String,
+    pub bybit_secret_key: String,
+    pub htx_access_key: String,
+    pub htx_secret_key: String,
+    pub htx_pass: String,
+}
+
+impl AccountInfo {
+    pub fn new() -> AccountInfo {
+        AccountInfo {
+            gate_access_key: "".to_string(),
+            gate_secret_key: "".to_string(),
+            binance_access_key: "".to_string(),
+            binance_secret_key: "".to_string(),
+            kucoin_access_key: "".to_string(),
+            kucoin_secret_key: "".to_string(),
+            kucoin_pass: "".to_string(),
+            okx_access_key: "".to_string(),
+            okx_secret_key: "".to_string(),
+            okx_pass: "".to_string(),
+            bitget_access_key: "".to_string(),
+            bitget_secret_key: "".to_string(),
+            bitget_pass: "".to_string(),
+            bybit_access_key: "".to_string(),
+            bybit_secret_key: "".to_string(),
+            htx_access_key: "".to_string(),
+            htx_secret_key: "".to_string(),
+            htx_pass: "".to_string(),
+        }
+    }
+}
+
+// 获取文件内容
+pub fn get_account_info(file_path: &str) -> AccountInfo {
+    let file = File::open(file_path);
+    let mut contents = String::new();
+    let result = match file {
+        Ok(mut value) => {
+            value.read_to_string(&mut contents).unwrap_or_default();
+            from_str(&contents).unwrap_or(AccountInfo::new())
+        }
+        Err(_) => {
+            error!("没有获取到账号配置文件!");
+            AccountInfo::new()
+        }
+    };
+    result
+}

+ 30 - 0
global/src/cci.rs

@@ -0,0 +1,30 @@
+use rust_decimal::Decimal;
+use serde_derive::Serialize;
+use crate::predictor_state::PredictorState;
+
+#[derive(Serialize, Clone, Debug)]
+pub struct CentralControlInfo {
+    pub now_balance: Decimal,                                       // 钱包余额
+    pub unrealized_pn_l: Decimal,                                   // 未实现盈亏
+    pub pos: Decimal,                                               // 持仓数量
+    pub entry_price: Decimal,                                       // 开仓价格
+    pub now_price: Decimal,                                         // 当前价格
+
+    pub predictor_state_vec: Vec<PredictorState>,                   // 模型状态
+}
+
+impl CentralControlInfo {
+    // 时间窗口大小(微秒)
+    // const MAX_TIME_RANGE_MICROS: i64 = 2 * 60 * 60_000_000;
+
+    pub fn new() -> Self {
+        Self {
+            now_balance: Default::default(),
+            unrealized_pn_l: Default::default(),
+            pos: Default::default(),
+            entry_price: Default::default(),
+            now_price: Default::default(),
+            predictor_state_vec: vec![],
+        }
+    }
+}

+ 133 - 0
global/src/clear_log_utils.rs

@@ -0,0 +1,133 @@
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::io;
+use tracing::{Event, info, Subscriber, warn};
+use tracing_appender_timezone::non_blocking::WorkerGuard;
+use tracing_subscriber::{fmt, Layer};
+use tracing_subscriber::layer::{Context, SubscriberExt};
+use reqwest::{Client};
+use tracing::field::{Field, Visit};
+use tracing_appender_timezone::rolling::{RollingFileAppender, Rotation};
+
+
+struct ErrorMessageVisitor {
+    message: String
+}
+
+impl Visit for ErrorMessageVisitor {
+    fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
+        if field.name() == "message" {
+            self.message = format!("{:?}", value);
+        }
+    }
+}
+
+
+// 错误报告发送到指定服务器
+struct ReportingLayer {
+    account_name: String,
+}
+impl<S> Layer<S> for ReportingLayer
+where
+    S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
+{
+    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
+        if event.metadata().level() == &tracing::Level::ERROR {
+            let mut visitor = ErrorMessageVisitor {
+                message: String::new()
+            };
+            event.record(&mut visitor);
+
+            let msg = format!("account={}, type=error, msg={}", self.account_name.clone(), visitor.message);
+            info!(msg)
+            // send_remote_err_log(msg)
+        }
+    }
+}
+
+pub fn send_remote_err_log(msg: String) {
+    tokio::spawn(async move {
+        let encoded_str = base64::encode(msg.clone());
+        let mut request_json_data = HashMap::new();
+        request_json_data.insert("serverName", "As");
+        request_json_data.insert("data", encoded_str.as_str());
+
+        let res = Client::new().post("https://hhh.liangjiang.cc/api/log/addError?key=d64a8sc874sa8c4as5")
+            .json(&request_json_data)
+            .send()
+            .await;
+
+        match res {
+            Ok(_resp) => {
+                // let body = _resp.text().await.unwrap();
+            }
+            Err(err) => {
+                warn!("log的error监听器发送远端报错失败:{:?}", err);
+            }
+        }
+    });
+}
+
+pub fn init_log_with_debug() {
+    let _ = final_init(tracing::Level::DEBUG.as_str(), 0, "test".to_string());
+}
+
+pub fn init_log_with_trace() {
+    let _ = final_init(tracing::Level::TRACE.as_str(), 0, "test".to_string());
+}
+
+pub fn init_log_with_info() {
+    let _ = final_init(tracing::Level::INFO.as_str(), 0, "test".to_string());
+}
+
+pub fn final_init(level: &str, port: u32, account_name: String) -> WorkerGuard {
+    let mut path = String::new();
+    path.push_str("./logs");
+    path.push_str(port.to_string().as_str());
+    path.push_str("/clear_program");
+
+    let file_appender = RollingFileAppender::builder()
+        .time_zone(8)
+        .rotation(Rotation::DAILY)
+        .filename_suffix("log")
+        .build(path)
+        .expect("initializing rolling file appender failed");
+    let (non_blocking, guard) = tracing_appender_timezone::non_blocking(file_appender);
+
+    use time::{macros::format_description, UtcOffset};
+    use tracing_subscriber::{fmt::time::OffsetTime};
+    let local_time = OffsetTime::new(
+        UtcOffset::from_hms(8, 0, 0).unwrap(),
+        format_description!("[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]"),
+    );
+
+    let fmt_layer = fmt::layer()
+        .with_timer(local_time.clone())
+        .with_target(true)
+        .with_line_number(true)
+        .with_level(true)
+        .with_writer(io::stdout)
+        .with_span_events(fmt::format::FmtSpan::FULL);
+
+    let file_layer = fmt::layer()
+        .with_timer(local_time.clone())
+        .with_target(true)
+        .with_ansi(false)
+        .with_level(true)
+        .with_writer(non_blocking.clone())
+        .with_span_events(fmt::format::FmtSpan::FULL);
+
+    let reporting_layer = ReportingLayer {
+        account_name
+    };
+
+    let layer = tracing_subscriber::Registry::default()
+        .with(fmt_layer)
+        .with(file_layer)
+        .with(reporting_layer)
+        .with(tracing_subscriber::EnvFilter::new(level));
+
+    tracing::subscriber::set_global_default(layer).unwrap();
+
+    return guard;
+}

+ 26 - 0
global/src/clear_position_result.rs

@@ -0,0 +1,26 @@
+use serde_derive::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ClearPositionResult {
+    pub r_id: String,
+    pub clear_order_num: String,
+    pub clear_order_str: String,
+    pub clear_position_num: String,
+    pub clear_position_str: String,
+    pub clear_other_err: bool,
+    pub clear_other_str: String
+}
+
+impl ClearPositionResult {
+    pub fn new() -> ClearPositionResult {
+        ClearPositionResult{
+            r_id: "".to_string(),
+            clear_order_num: "0".to_string(),
+            clear_order_str: "".to_string(),
+            clear_position_num: "0".to_string(),
+            clear_position_str: "".to_string(),
+            clear_other_err: false,
+            clear_other_str: "".to_string(),
+        }
+    }
+}

+ 76 - 0
global/src/export_utils.rs

@@ -0,0 +1,76 @@
+use chrono::Local;
+use simple_excel_writer::*;
+use uuid::Uuid;
+
+pub fn export_excel(header_array: Vec<&str>, data: Vec<Vec<String>>, prefix_name: &str) -> String {
+    //本地存储路径
+    let save_path = "C:/Users/Public/Documents/";
+    let _ = std::fs::create_dir(format!("{}rust_export/", save_path));
+    // 当前时间
+    let date = Local::now().format("%Y-%m-%d").to_string();
+    let uid = Uuid::new_v4().to_string()[0..8].to_string();
+    let name = if prefix_name == "" { "".to_string() } else { format!("{}_", prefix_name) };
+    let file_name = format!("{}rust_export/{}{}_{}.xlsx", save_path, name, date, uid);
+    let mut wb = Workbook::create(&file_name);
+    let mut sheet = wb.create_sheet("sheet1");
+    // 设置行宽
+    for _ in 0..header_array.len() {
+        sheet.add_column(Column { width: 30.0 });
+    }
+    wb.write_sheet(&mut sheet, |sheet_writer| {
+        let sw = sheet_writer;
+        let mut header_row = Row::new();
+        for value in header_array.clone() {
+            header_row.add_cell(value);
+        }
+        sw.append_row(header_row).expect(format!("创建excel标题错误!header:{:?}", header_array).as_str());
+        for cell in data.clone() {
+            let mut data_row = Row::new();
+            for value in cell.clone() {
+                data_row.add_cell(value);
+            }
+            sw.append_row(data_row).expect(format!("添加excel数据错误!cell:{:?}", cell).as_str());
+        }
+        sw.append_row(Default::default())
+    }).expect("写入excel错误!");
+    wb.close().expect("关闭excel错误!");
+    file_name
+}
+
+pub fn export_excel_sheets(header_array: Vec<Vec<&str>>, data: Vec<Vec<Vec<String>>>, sheet_name: Vec<&str>, prefix_name: &str) -> String {
+    //本地存储路径
+    let save_path = "C:/Users/Public/Documents/";
+    let _ = std::fs::create_dir(format!("{}rust_export/", save_path));
+    // 当前时间
+    let date = Local::now().format("%Y-%m-%d").to_string();
+    let uid = Uuid::new_v4().to_string()[0..8].to_string();
+    let name = if prefix_name == "" { "".to_string() } else { format!("{}_", prefix_name) };
+    let file_name = format!("{}rust_export/{}{}_{}.xlsx", save_path, name, date, uid);
+    let mut wb = Workbook::create(&file_name);
+    if header_array.len() != data.len() { return "表头和数据数量有误,请检查后再试!".to_string(); }
+    for (index, headers) in header_array.iter().enumerate() {
+        let mut sheet = wb.create_sheet(sheet_name[index]);
+        // 设置行宽
+        for _ in 0..headers.len() {
+            sheet.add_column(Column { width: 20.0 });
+        }
+        wb.write_sheet(&mut sheet, |sheet_writer| {
+            let sw = sheet_writer;
+            let mut header_row = Row::new();
+            for value in headers.clone() {
+                header_row.add_cell(value);
+            }
+            sw.append_row(header_row).expect(format!("创建excel标题错误!header:{:?}", header_array).as_str());
+            for cell in data[index].clone() {
+                let mut data_row = Row::new();
+                for value in cell.clone() {
+                    data_row.add_cell(value);
+                }
+                sw.append_row(data_row).expect(format!("添加excel数据错误!cell:{:?}", cell).as_str());
+            }
+            sw.append_row(Default::default())
+        }).expect("写入excel错误!");
+    }
+    wb.close().expect("关闭excel错误!");
+    file_name
+}

+ 42 - 0
global/src/fixed_time_range_deque.rs

@@ -0,0 +1,42 @@
+use std::collections::VecDeque;
+use chrono::Utc;
+use serde_derive::{Deserialize, Serialize};
+
+// 定制的队列,可以统一指定长度
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct FixedTimeRangeDeque<T> {
+    pub deque: VecDeque<T>,
+    pub deque_t: VecDeque<i64>,
+    pub time_range: i64,
+}
+
+impl<T> FixedTimeRangeDeque<T> {
+    pub fn new(range: i64) -> Self {
+        FixedTimeRangeDeque {
+            deque: VecDeque::new(),
+            deque_t: VecDeque::new(),
+            time_range: range
+        }
+    }
+
+    pub fn push_back(&mut self, value: T) {
+        let now = Utc::now().timestamp_micros();
+
+        self.deque.push_back(value);
+        self.deque_t.push_back(now);
+
+        // =================== 检查长度,如果超过时间窗口,则移除前端元素 ==================
+        while self.deque_t.len() > 0 && now - self.deque_t.get(0).unwrap() > self.time_range {
+            self.deque.pop_front();
+            self.deque_t.pop_front();
+        }
+    }
+
+    pub fn len(&self) -> usize {
+        self.deque.len()
+    }
+
+    pub fn get(&self, index: usize) -> Option<&T> {
+        self.deque.get(index)
+    }
+}

+ 12 - 0
global/src/lib.rs

@@ -0,0 +1,12 @@
+pub mod public_params;
+pub mod log_utils;
+pub mod params;
+pub mod trace_stack;
+pub mod export_utils;
+pub mod account_info;
+pub mod cci;
+pub mod clear_position_result;
+pub mod trade;
+pub mod clear_log_utils;
+pub mod fixed_time_range_deque;
+pub mod predictor_state;

+ 106 - 0
global/src/log_utils.rs

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

+ 117 - 0
global/src/params.rs

@@ -0,0 +1,117 @@
+use std::fs::File;
+use std::io::Read;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use toml::from_str;
+use serde_derive::Deserialize;
+use serde_json::Value;
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Params {
+    // 经纪商id
+    pub broker_id: String,
+    // 账号昵称
+    pub account_name: String,
+    // ak
+    pub access_key: String,
+    // sk
+    pub secret_key: String,
+    // pk
+    pub pass_key: String,
+    // 交易盘口
+    pub exchange: String,
+    // 交易币对
+    pub pair: String,
+    // 开仓
+    pub open: Decimal,
+    // 激活开仓挂单
+    pub open_activate: Decimal,
+    // 激活开仓挂单的最小spread
+    pub min_spread: Decimal,
+    // 激活平仓挂单
+    pub close_activate: Decimal,
+    // 平仓
+    pub close: Decimal,
+    // 杠杆大小
+    pub lever_rate: Decimal,
+    // 现货底仓
+    pub hold_coin: Decimal,
+    // core的run_strategy函数,用于定期检查使用
+    pub interval: u64,
+    // 参考盘口
+    pub ref_exchange: Vec<String>,
+    // 参考币种
+    pub ref_pair: Vec<String>,
+    // 账户资金使用比例
+    pub used_pct: Decimal,
+    // 止损比例 默认0.02  0.02 = 2%
+    pub stop_loss: Decimal,
+    // 平滑系数 默认0.999
+    pub gamma: Decimal,
+    // 分批建仓功能 小资金建议1 大资金建议3 默认 1
+    pub grid: i8,
+    // 是否启用colocation技术, 1开启,0关闭 默认0
+    pub colo: i8,
+    // 日志级别,从低到高依次是:[trace, debug, info, warn, error]
+    pub log_level: String,
+    // 中控端口
+    pub port: u32,
+    // 运行模式 0.正常策略运行, 1.清理挂单及仓位
+    pub run_mode: i8,
+    // 机器人id
+    pub r_id: String,
+}
+
+impl Params {
+    pub fn new(file_path: &str) -> Result<Params, Box<dyn std::error::Error>> {
+        // 打开文件并读取内容
+        let mut file = File::open(file_path)?;
+        let mut contents = String::new();
+        file.read_to_string(&mut contents)?;
+
+        // 解析 TOML 数据到 Params 结构体
+        let params: Params = from_str(&contents)?;
+
+        Ok(params)
+    }
+    pub fn new_json(file_path: &str, call_port: u32) -> Result<Params, Box<dyn std::error::Error>> {
+        // 打开文件并读取内容
+        let json_contents = std::fs::read_to_string(file_path)?;
+
+        let json_value: Value = serde_json::from_str(&json_contents).unwrap();
+        let acc = json_value["account"].clone();
+
+        // 使用serde_json库来解析JSON文件内容,并将其转换为Ship结构体
+        // let params: Params = serde_json::from_str(&json_contents)?;
+        let params: Params = Params {
+            account_name: acc["name"].as_str().unwrap().to_string(),
+            access_key: acc["accessKey"].as_str().unwrap().to_string(),
+            secret_key: acc["secretKey"].as_str().unwrap().to_string(),
+            pass_key: acc["pass"].as_str().unwrap().to_string(),
+            exchange: json_value["exchange"].as_str().unwrap().to_string(),
+            pair: json_value["pair"].as_str().unwrap().to_string(),
+            open: Decimal::try_from(json_value["open"].as_f64().unwrap_or_default()).unwrap(),
+            open_activate: Decimal::try_from(json_value["open_activate"].as_f64().unwrap_or_default()).unwrap(),
+            min_spread: Decimal::try_from(json_value["min_spread"].as_f64().unwrap_or_default()).unwrap(),
+            close: Decimal::try_from(json_value["close"].as_f64().unwrap_or_default()).unwrap(),
+            lever_rate: Decimal::try_from(json_value["lever_rate"].as_f64().unwrap_or_default()).unwrap(),
+            ref_exchange: vec![json_value["ref_exchange"].as_str().unwrap().to_string()],                   // 兼容新版本与老版本
+            ref_pair: vec![json_value["ref_pair"].as_str().unwrap().to_string()],                           // 兼容新版本与老版本
+            stop_loss: Decimal::try_from(json_value["stop_loss"].as_f64().unwrap_or_default()).unwrap(),
+            // 接下来是写死的参数
+            close_activate: Decimal::ZERO,
+            hold_coin: Decimal::ZERO,
+            grid: 1,
+            colo: 0,
+            interval: 100,
+            broker_id: json_value["exchange"].as_str().unwrap().to_string().split("_").collect::<Vec<_>>()[0].to_string(),
+            used_pct: dec!(1),
+            gamma: dec!(0.999),
+            log_level: "info".to_string(),
+            port: call_port,
+            run_mode: 0,
+            r_id: "-1".to_string()
+        };
+        Ok(params)
+    }
+}

+ 25 - 0
global/src/predictor_state.rs

@@ -0,0 +1,25 @@
+use rust_decimal::Decimal;
+use serde_derive::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct PredictorState {
+    pub update_time: Decimal,                                                   // 该条数据的时间
+
+    pub mid_price: Decimal,                                                     // 中间价
+    pub ask_price: Decimal,                                                     // 卖一价
+    pub last_price: Decimal,                                                    // 最后成交价格
+    pub bid_price: Decimal,                                                     // 买一价
+    pub optimal_ask_price: Decimal,                                             // 卖出挂单价
+    pub optimal_bid_price: Decimal,                                             // 买入挂单价
+    pub ref_price: Decimal,                                                     // 公平价格
+
+    pub spread: Decimal,                                                        // 市场冲击
+    pub flow_ratio: Decimal,                                                    // 资金流
+    pub spread_max: Decimal,                                                    // 最大市场冲击
+    pub spread_min: Decimal,                                                    // 最小市场冲击
+
+    pub inventory: Decimal,                                                     // 库存,也就是q
+    pub sigma_square: Decimal,                                                  // σ^2,波动性的平方
+    pub gamma: Decimal,                                                         // γ,库存风险厌恶参数
+    pub kappa: Decimal,                                                         // κ 订单簿 流动性 参数
+}

+ 35 - 0
global/src/public_params.rs

@@ -0,0 +1,35 @@
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+
+// TODO 市场数据汇总(market_info)的下标集合
+pub const LEVEL: usize = 1;
+pub const TRADE_LENGTH: usize = 2;                                          // 推测应该是交易交易所的数据长度
+pub const LENGTH: usize = LEVEL * 4 + TRADE_LENGTH;                         // 市场数据汇总的总长度
+pub const BID_PRICE_INDEX: usize = LEVEL * 0;                               // 买入价格下标
+pub const BID_QUANTITY_INDEX: usize = LEVEL * 0 + 1;                        // 买入数量下标
+pub const ASK_PRICE_INDEX: usize = LEVEL * 2;                               // 卖出价格下标
+pub const ASK_QUANTITY_INDEX: usize = LEVEL * 2 + 1;                        // 卖出数量下标
+// 上面是市场数据汇总的下标相关
+pub const MARKET_DELAY_LIMIT: i64 = 10*60*1000;                             // 市场信息延迟限制(单位:毫秒)
+pub const GRID: i64 = 1;                                                    // 策略资金分成多少份
+pub const STOP_LOSS: Decimal = dec!(0.02);                                  // 风控止损比例,0.02代表2%,是原文的STOPLOSS
+pub const GAMMA: Decimal = dec!(0.999);                                     // gamma默认值
+pub const EFF_RANGE: Decimal = dec!(0.001);                                 // 每1权重需要多少价格距离,0.001代表0.1%,每0.1%代表1权重
+
+// 各交易所限频规则汇总
+pub const BASIC_LIMIT:i64 = 100;
+pub const GATE_SPOT_LIMIT:i64 = 10;
+pub const GATE_USDT_SWAP_LIMIT:i64 = 100;
+pub const KUCOIN_SPOT_LIMIT:i64 = 15;
+pub const KUCOIN_USDT_SWAP_LIMIT:i64 = 7;
+pub const BINANCE_USDT_SWAP_LIMIT:i64 = 10;
+pub const BINANCE_SPOT_LIMIT:i64 = 2;
+pub const COINEX_SPOT_LIMIT:i64 = 20;
+pub const COINEX_USDT_SWAP_LIMIT:i64 = 15;
+pub const OKEX_USDT_SWAP_LIMIT:i64 = 30;
+pub const BITGET_USDT_SWAP_LIMIT:i64 = 10;
+pub const BITGET_USDT_SPOT_LIMIT:i64 = 100;
+pub const BYBIT_USDT_SWAP_LIMIT:i64 = 10;
+pub const HTX_USDT_SWAP_LIMIT:i64 = 24;
+pub const MEXC_SPOT_LIMIT:i64 = 333;
+pub const RATIO:i64 = 4;

+ 162 - 0
global/src/trace_stack.rs

@@ -0,0 +1,162 @@
+use std::fmt;
+use std::fmt::{Formatter};
+use chrono::Utc;
+use rust_decimal::prelude::ToPrimitive;
+use tokio::time::Instant;
+use tracing::info;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TraceStack {
+    pub before_network: i64,                // 官方数据生成时间
+    pub after_network: i64,                 // 到达网络层时间
+
+    pub ins: Instant,                       // 到达网络层的Instant,用于本地各层耗时计算。
+
+    pub after_span_line: u128,              // 跨线程之后
+
+    pub after_unlock_core: u128,            // 解锁core之后
+
+    pub after_format: u128,                 // 结束格式化之后
+
+    pub after_strategy: u128,               // 逻辑层结束
+
+    pub before_send: u128,                  // 发送指令时时间
+    pub after_send: u128,                   // 发送指令结束时间
+
+    pub source: String,                     // 订单来源[depth|order]
+    pub order_command: String,              // 订单发送时的command
+}
+
+impl TraceStack {
+    pub fn new(after_network: i64, after_network_instant: Instant) -> Self {
+        TraceStack {
+            before_network: 0,
+            after_network,
+            ins: after_network_instant,
+            after_span_line: 0,
+            after_unlock_core: 0,
+            after_format: 0,
+            after_strategy: 0,
+            before_send: 0,
+            after_send: 0,
+            source: "".to_string(),
+            order_command: "".to_string(),
+        }
+    }
+
+    pub fn show_delay(ins: &Instant) {
+        pub static mut COUNT: u128 = 0u128;
+        pub static mut SUM_DELAY: u128 = 0u128;
+        pub static mut PREV_LOG_TIMESTAMP: i64 = 0;
+
+        unsafe {
+            let delay = ins.elapsed().as_nanos();
+            COUNT += 1;
+            SUM_DELAY += delay;
+
+            // 30s打印一次
+            let now = Utc::now().timestamp_millis();
+            if now - PREV_LOG_TIMESTAMP > 30 * 1000 {
+                PREV_LOG_TIMESTAMP = now;
+                info!("数据{}条, avg={}ns", COUNT, SUM_DELAY / COUNT);
+            }
+
+            // 总延迟满3亿ns清理一次
+            if SUM_DELAY > 300_000_000 {
+                COUNT = 0;
+                SUM_DELAY = 0;
+            }
+        }
+    }
+
+    pub fn on_before_network(&mut self, before_network_millis: i64) {
+        self.before_network = before_network_millis;
+    }
+
+    pub fn on_after_network(&mut self, after_network: i64) {
+        self.after_network = after_network;
+    }
+
+    pub fn on_after_span_line(&mut self) {
+        self.after_span_line = self.ins.elapsed().as_nanos();
+    }
+
+    pub fn on_after_unlock_core(&mut self) {
+        self.after_unlock_core = self.ins.elapsed().as_nanos();
+    }
+
+    pub fn on_after_format(&mut self) {
+        self.after_format = self.ins.elapsed().as_nanos();
+    }
+
+    pub fn on_after_strategy(&mut self) {
+        self.after_strategy = self.ins.elapsed().as_nanos();
+    }
+
+    pub fn on_before_send(&mut self) {
+        self.before_send = self.ins.elapsed().as_nanos();
+    }
+
+    pub fn on_after_send(&mut self) {
+        self.after_send = self.ins.elapsed().as_nanos();
+    }
+
+    pub fn set_order_command(&mut self, command_str: String) {
+        self.order_command = command_str;
+    }
+
+    pub fn on_special(mut self, event_str: String) -> Self {
+        self.after_network = 1;
+        self.source = event_str;
+
+        self
+    }
+
+    pub fn set_source(&mut self, source: String) {
+        self.source = source
+    }
+}
+
+impl fmt::Display for TraceStack {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let mut msg = String::new();
+
+        if !self.source.is_empty() {
+            msg.push_str(format!("订单来源:{} ", self.source).as_str());
+        }
+
+        if self.before_send != 0 {
+            msg.push_str(format!("本地总耗时{}ns  ", self.before_send).as_str());
+        }
+
+        if self.before_network != 0 && self.after_network != 0 {
+            msg.push_str(format!("数据到达rust耗时{}ms  ", (self.after_network - self.before_network).to_f64().unwrap() / 1000.0).as_str());
+        }
+
+        if self.after_span_line != 0 {
+            msg.push_str(format!("跨协程完毕{}ns  ", self.after_span_line).as_str());
+        }
+
+        if self.after_format != 0 {
+            msg.push_str(format!("数据格式化完毕{}ns  ", self.after_format).as_str());
+        }
+
+        if self.after_unlock_core != 0 {
+            msg.push_str(format!("解锁完毕{}ns  ", self.after_unlock_core).as_str());
+        }
+
+        if self.after_strategy != 0 {
+            msg.push_str(format!("计算层完毕{}ns  ", self.after_strategy).as_str());
+        }
+
+        if self.after_send != 0 && self.before_send != 0 {
+            msg.push_str(format!("发送订单耗时(发送-服务器处理-响应到本地){}ms  ", (self.after_send - self.before_send).to_f64().unwrap() / 1e6f64).as_str());
+        }
+
+        // if self.after_send != 0 && self.after_network != 0 {
+        //     msg.push_str(format!("总共耗时{}毫秒", (self.after_send - self.after_network).to_f64().unwrap() / 1000.0).as_str());
+        // }
+
+        write!(f, "{}", msg)
+    }
+}

+ 17 - 0
global/src/trade.rs

@@ -0,0 +1,17 @@
+use rust_decimal::Decimal;
+
+pub struct Trade {
+    // 价格
+    pub price: Decimal,
+    // 时间
+    pub time: i64
+}
+
+impl Trade {
+    pub fn new_by_ticker (price: Decimal, time: i64) -> Trade {
+        Trade{
+            price,
+            time
+        }
+    }
+}

+ 10 - 0
global/tests/get_account_info_test.rs

@@ -0,0 +1,10 @@
+use tracing::trace;
+use global::account_info;
+
+#[tokio::test]
+async fn get_account_info() {
+    global::log_utils::init_log_with_trace();
+
+    let account_info = account_info::get_account_info("../test_account.toml");
+    trace!(?account_info);
+}

+ 467 - 0
phemex_swap_rest.rs

@@ -0,0 +1,467 @@
+use std::collections::BTreeMap;
+
+use chrono::Utc;
+use reqwest::{Client, Proxy};
+use reqwest::header::HeaderMap;
+use ring::hmac;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::{json, Value};
+use tracing::{error, info, trace};
+
+use crate::http_tool::RestTool;
+use crate::proxy;
+use crate::response_base::ResponseData;
+
+#[derive(Clone, Debug)]
+pub struct PhemexSwapRest {
+    pub tag: String,
+    base_url: String,
+    /*******参数*/
+    //是否需要登录
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+
+}
+
+impl PhemexSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> PhemexSwapRest
+    {
+        return PhemexSwapRest::new_with_tag("default-PhemexSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: BTreeMap<String, String>) -> PhemexSwapRest {
+        let base_url = if is_colo {
+            let z = "https://api.phemex.com".to_string();
+            info!("开启高速(未配置,走普通:{})通道",z);
+            z
+        } else {
+            let z = "https://api.phemex.com".to_string();
+            info!("走普通通道:{}",z);
+            z
+        };
+
+
+        /*****返回结构体*******/
+        PhemexSwapRest {
+            tag,
+            base_url,
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //服务器时间
+    pub async fn get_server(&mut self) -> ResponseData {
+        let params = json!({});
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/public/time".to_string(),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //查詢合約基礎信息
+    pub async fn get_market(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/public/products".to_string(),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+    //查詢ticker(Query 24 ticker)
+    pub async fn get_ticker(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/md/v3/ticker/24hr".to_string(),
+                                false,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //持仓(查询交易账户和仓位)
+    pub async fn get_account_and_positions(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/g-accounts/accountPositions".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //仓位设置(Switch Position Mode Synchronously)
+    pub async fn set_target_pos_mode(&mut self, params: Value) -> ResponseData {
+        let data = self.request("PUT".to_string(),
+                                "".to_string(),
+                                "/g-positions/switch-pos-mode-sync".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //设置杠杆(Set Leverage 设置杠杆)
+    pub async fn set_leverage(&mut self, params: Value) -> ResponseData {
+        let data = self.request("PUT".to_string(),
+                                "".to_string(),
+                                "/g-positions/leverage".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //下单
+    pub async fn orders(&mut self, params: Value) -> ResponseData {
+        let data = self.request("PUT".to_string(),
+                                "".to_string(),
+                                "/g-orders/create".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+
+    //撤单
+    pub async fn cancel_order(&mut self, params: Value) -> ResponseData {
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                "/g-orders/cancel".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //撤销所有
+    pub async fn cancel_order_all(&mut self, params: Value) -> ResponseData {
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                "/g-orders/all".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //订单列表
+    pub async fn get_orders(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/api-data/g-futures/orders".to_string(),
+                                true,
+                                params,
+        ).await;
+        data
+    }
+
+    //根据id查询订单
+    pub async fn get_orders_by_id(&mut self, params: Value) -> ResponseData {
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                "/api-data/g-futures/orders/by-order-id".to_string(),
+                                true,
+                                params,
+        ).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_json: Value) -> 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 timestamp = (Utc::now().timestamp_millis() + (60 * 1000)) / 1000;
+
+        //请求类型不同,可能请求头body 不同
+        let mut body = "".to_string();
+        let mut params = "".to_string();
+        let mut headers = HeaderMap::new();
+        if method == "POST" {
+            body = params_json.to_string();
+        }
+        if method == "GET" || method == "PUT" || method == "DELETE" {
+            let z = params_json.to_string();
+            params = RestTool::parse_params_to_str(z);
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {
+                //需要登录-且登录参数齐全
+                trace!("Path:{}{}", prefix_url.clone(),request_url.clone());
+                trace!("Query:{}", params);
+                trace!("Body:{}", body);
+                trace!("expire:{}", timestamp.to_string());
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      prefix_url.clone(),
+                                      request_url.clone(),
+                                      params.clone(),
+                                      body.clone(),
+                                      timestamp.to_string(),
+                );
+                trace!("Signature:{}", sing);
+                //组装header
+                headers.extend(Self::headers(sing, timestamp.to_string(), access_key));
+            }
+        }
+
+
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response = self.http_tool(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.clone(),
+            body.clone(),
+            headers,
+            is_login,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+    }
+
+    pub fn headers(sign: String, timestamp: String, access_key: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("x-phemex-access-token", access_key.parse().unwrap());// 这是 Phemex 网站的 API-KEY(id 字段)
+        headers.insert("x-phemex-request-expiry", timestamp.parse().unwrap());// 描述请求过期的 Unix EPoch 秒数,通常应为 (Now() + 1 分钟)
+        headers.insert("x-phemex-request-signature", sign.parse().unwrap());// 这是 http 请求的 HMAC SHA256 签名。Secret 是 API Secret,其公式为:HMacSha256(URL Path + QueryString + Expiry + body)
+        // let tracing = format!("4l_{:?}", Utc::now().timestamp_millis().to_string());
+        // headers.insert("x-phemex-request-tracing", tracing.parse().unwrap());
+        headers
+    }
+    pub fn sign(secret_key: String,
+                prefix_url: String, request_url: String,
+                url_param_str: String, body: String, timestamp: String) -> String
+    {
+        /*签名生成*/
+        let base_url = format!("{}{}", prefix_url, request_url);
+        // HMacSha256(URL Path + QueryString + Expiry + body)
+
+        // 时间戳 + 请求类型+ 请求参数字符串
+        let message = format!("{}{}{}{}", base_url, url_param_str, timestamp, body);
+        trace!("message:{}",message);
+
+        // 做签名
+        let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+        let sign = hex::encode(hmac::sign(&hmac_key, message.as_bytes()).as_ref());
+        sign
+    }
+
+    // async fn http_tool(&mut self, request_path: String, request_type: String, params: String, headers: HeaderMap) -> Result<ResponseData, reqwest::Error> {
+    async fn http_tool(&mut self, request_path: String,
+                       request_type: String,
+                       params: String,
+                       body: String,
+                       headers: HeaderMap,
+                       is_login: bool) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let addrs_url: String = if params == "" {
+            url.clone()
+        } else {
+            format!("{}?{}", url.clone(), params)
+        };
+
+        trace!("url-----:{}",url.clone());
+        trace!("addrs_url-----:{}",addrs_url.clone());
+        trace!("params-----:{}",params.clone());
+        trace!("body-----:{}",body.clone());
+        trace!("headers-----:{:?}",headers.clone());
+        trace!("request_type-----:{:?}",request_type.clone());
+
+
+        let client = if is_login {
+            let params = proxy::ParsingDetail::http_enable_proxy(Some("phemex"));
+            let client_re;
+            if params {
+                let proxy_address = "socks5://127.0.0.1:17890"; // 替换为你的 SOCKS5 代理地址
+                let proxy = Proxy::all(proxy_address).unwrap();
+                client_re = Client::builder().proxy(proxy).build().unwrap();
+            } else {
+                client_re =  Client::new()
+            }
+            client_re
+        } else {
+            proxy::ParsingDetail::http_enable_proxy(None);
+            Client::new()
+        };
+        let request_builder = match request_type.as_str() {
+            "GET" => client.get(addrs_url.clone()).headers(headers),
+            "POST" => client.post(url.clone()).body(body).headers(headers),
+            "DELETE" => client.delete(addrs_url.clone()).headers(headers),
+            "PUT" => client.put(addrs_url.clone()).headers(headers),
+            _ => {
+                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();
+        // trace!("text:{:?}",text);
+        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 json_value = serde_json::from_str::<Value>(&text).unwrap();
+        // return  ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value);
+
+        let id = json_value["id"].as_i64();
+        match id {
+            None => {}
+            Some(v) => {
+                match v {
+                    0 => {
+                        let result = json_value.get("result").unwrap();
+                        return ResponseData::new(self.tag.clone(), 200, "success".to_string(), result.clone());
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        let code = json_value["code"].as_i64();
+        match code {
+            None => {}
+            Some(v) => {
+                match v {
+                    0 => {
+                        //判断是否有code ,没有表示特殊接口,直接返回
+                        if json_value.get("data").is_some() {
+                            let data = json_value.get("data").unwrap();
+                            return ResponseData::new(self.tag.clone(), 200, "success".to_string(), data.clone());
+                        } else {
+                            return ResponseData::new(self.tag.clone(), 200, "success".to_string(), json_value);
+                        }
+                    }
+                    _ => {
+                        if json_value.get("msg").is_some() {
+                            return ResponseData::new(self.tag.clone(), 400, format!("{:?}", json_value["msg"].as_str()), json_value);
+                        } else {
+                            return ResponseData::new(self.tag.clone(), 400, "error".to_string(), json_value);
+                        }
+                    }
+                }
+            }
+        }
+
+
+        return ResponseData::new(self.tag.clone(), 400, "error".to_string(), json_value);
+    }
+
+    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 code = data["code"].as_i64();
+                match code {
+                    None => {}
+                    Some(v) => {
+                        match v {
+                            _ => {
+                                if data.get("msg").is_some() {
+                                    return ResponseData::new(self.tag.clone(), 400, format!("{:?}", data["msg"].as_str().unwrap()), data);
+                                } else {
+                                    return ResponseData::new(self.tag.clone(), 400, "error".to_string(), data);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                return ResponseData::error("".to_string(),
+                                           format!("json 解析失败:{},相关参数:{}", e, text));
+            }
+        }
+        return ResponseData::error("".to_string(), format!("请求失败:{:?}", text));
+    }
+}

+ 1 - 0
rust-toolchain

@@ -0,0 +1 @@
+1.83.0

+ 36 - 0
src/clear_core_libs.rs

@@ -0,0 +1,36 @@
+
+use std::collections::BTreeMap;
+use std::io::Error;
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool};
+use tokio::sync::{mpsc, Mutex};
+use tracing::{info};
+use global::cci::CentralControlInfo;
+use global::params::Params;
+use standard::Order;
+use strategy::clear_core::ClearCore;
+
+pub async fn init(params: Params,
+                  _ws_running: Arc<AtomicBool>,
+                  running: Arc<AtomicBool>,
+                  cci_arc: Arc<Mutex<CentralControlInfo>>) -> ClearCore {
+    // 封装
+    let mut exchange_params:BTreeMap<String, String> = BTreeMap::new();
+    exchange_params.insert("access_key".to_string(), params.access_key.clone());
+    exchange_params.insert("secret_key".to_string(), params.secret_key.clone());
+    exchange_params.insert("pass_key".to_string(), params.pass_key.clone());
+
+    let (order_sender, _order_receiver) = mpsc::channel::<Order>(100);
+    let (error_sender, _error_receiver) = mpsc::channel::<Error>(100);
+
+    let mut core_obj = ClearCore::new(params.exchange.clone(),
+                                 params.clone(),
+                                 exchange_params.clone(),
+                                 order_sender.clone(),
+                                 error_sender.clone(),
+                                 running.clone(),
+                                 cci_arc.clone()).await;
+    info!("清仓检查程序ClearCore初始化……");
+    core_obj.before_trade().await;
+    return core_obj;
+}

+ 12 - 0
src/control_c.rs

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

+ 149 - 0
src/core_libs.rs

@@ -0,0 +1,149 @@
+
+use strategy::core::Core;
+use std::collections::BTreeMap;
+use std::io::Error;
+use strategy::{exchange_disguise, core};
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool};
+use std::time::Duration;
+use chrono::Utc;
+use tokio::sync::{mpsc, Mutex};
+use tokio::time::Instant;
+use tracing::{error, info};
+use global::cci::CentralControlInfo;
+use global::params::Params;
+use global::trace_stack::TraceStack;
+use standard::Order;
+use strategy::model::OrderInfo;
+
+pub async fn init(params: Params,
+                  ws_running: Arc<AtomicBool>,
+                  running: Arc<AtomicBool>,
+                  cci_arc: Arc<Mutex<CentralControlInfo>>) -> Arc<Mutex<Core>> {
+    // 封装
+    let mut exchange_params:BTreeMap<String, String> = BTreeMap::new();
+    exchange_params.insert("access_key".to_string(), params.access_key.clone());
+    exchange_params.insert("secret_key".to_string(), params.secret_key.clone());
+    exchange_params.insert("pass_key".to_string(), params.pass_key.clone());
+
+    let (order_sender, mut order_receiver) = mpsc::channel::<Order>(100);
+    let (error_sender, mut error_receiver) = mpsc::channel::<Error>(100);
+
+    let mut core_obj = Core::new(params.exchange.clone(),
+                                   params.clone(),
+                                   exchange_params.clone(),
+                                   order_sender.clone(),
+                                   error_sender.clone(),
+                                   running.clone(),
+                                   cci_arc.clone()).await;
+    let ref_name = core_obj.ref_name[0].clone();
+    let trade_name = core_obj.trade_name.clone();
+
+    info!("core初始化……");
+    core_obj.before_trade().await;
+    let core_arc = Arc::new(Mutex::new(core_obj));
+
+    // 参考交易所
+    exchange_disguise::run_reference_exchange(ws_running.clone(),
+                                              params.ref_exchange.get(0).unwrap().clone(),
+                                              core_arc.clone(),
+                                              ref_name,
+                                              params.ref_pair.clone(),
+                                              params.colo != 0i8,
+                                              exchange_params.clone()).await;
+    // 交易交易所
+    exchange_disguise::run_transactional_exchange(ws_running.clone(),
+                                                  params.exchange,
+                                                  core_arc.clone(),
+                                                  trade_name,
+                                                  vec![params.pair.clone()],
+                                                  params.colo != 0i8,
+                                                  exchange_params.clone()).await;
+    // 启动定期触发的系统逻辑
+    core::on_timer(core_arc.clone());
+    // 启动策略逻辑
+    core::run_strategy(core_arc.clone());
+    info!("core初始化完成。");
+
+    let order_handler_core_arc = core_arc.clone();
+    tokio::spawn(async move {
+        loop {
+            tokio::time::sleep(Duration::from_millis(1)).await;
+
+            match order_receiver.recv().await {
+                Some(order) => {
+                    // 刚下的订单有可能会成交,所以有几率触发后续订单逻辑,所以这里也是一个订单触发逻辑
+                    let trace_stack = TraceStack::new(Utc::now().timestamp_micros(), Instant::now());
+
+                    if order.status != "NULL" {
+                        // trace_stack.on_before_format();
+                        let mut core = order_handler_core_arc.lock().await;
+                        // let mut delay_time_lock_instance = delay_time_lock.lock().await;
+                        let order_info = OrderInfo {
+                            symbol: "".to_string(),
+                            amount: order.amount.abs(),
+                            side: "".to_string(),
+                            price: order.price,
+                            client_id: order.custom_id,
+                            filled_price: order.avg_price,
+                            filled: order.deal_amount.abs(),
+                            order_id: order.id,
+                            local_time: 0,
+                            create_time: 0,
+                            status: order.status,
+                            fee: Default::default(),
+                            trace_stack: order.trace_stack.clone(),
+                        };
+                        // trace_stack.on_after_format();
+
+                        core.update_local_order(order_info.clone(), trace_stack).await;
+                    }
+                },
+                None => {
+                    error!("Order channel has been closed!");
+                }
+            }
+        }
+    });
+
+    let _error_handler_core_arc = core_arc.clone();
+    tokio::spawn(async move {
+        loop {
+            tokio::time::sleep(Duration::from_millis(1)).await;
+
+            match error_receiver.recv().await {
+                Some(_error) => {
+                    // let mut core = _error_handler_core_arc.lock().await;
+                    // error!("main: 订单出现错误{:?}", _error);
+                    // core.strategy._print_summary();
+                },
+                None => {
+                    error!("Error channel has been closed!");
+                }
+            }
+        }
+    });
+
+    // 定时仓位检测
+    // let markt_price_core_arc = core_arc.clone();
+    // tokio::spawn(async move {
+    //     info!("rest仓位检测定时任务启动(5s)...");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_secs(5)).await;
+    //
+    //         let mut core = markt_price_core_arc.lock().await;
+    //         match core.platform_rest.get_positions().await {
+    //             Ok(pos) => {
+    //                 if pos.len() > 0 {
+    //                     core.update_position(pos).await;
+    //                 }
+    //             },
+    //             Err(err) => {
+    //                 error!("rest持仓数据获取异常 {}", err);
+    //             }
+    //         };
+    //     }
+    // });
+
+    return core_arc;
+}

+ 197 - 0
src/main.rs

@@ -0,0 +1,197 @@
+mod server;
+mod control_c;
+mod core_libs;
+mod clear_core_libs;
+
+use std::str::FromStr;
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::time::Duration;
+use backtrace::Backtrace;
+use tokio::sync::Mutex;
+use tracing::{error, info, warn};
+use tracing_appender_timezone::non_blocking::WorkerGuard;
+use global::cci::CentralControlInfo;
+use global::params::Params;
+
+// 日志级别配置
+fn log_level_init(log_str: String, port: u32) -> WorkerGuard {
+    let log = global::log_utils::final_init(log_str.as_str(), port);
+    info!("日志级别读取成功:{}。", log_str);
+    return log;
+}
+
+// 清仓程序日志级别配置
+fn clear_log_level_init(log_str: String, port: u32, account_name: String) -> WorkerGuard {
+    let log = global::clear_log_utils::final_init(log_str.as_str(), port, account_name);
+    info!("清仓程序日志级别读取成功:{}。", log_str);
+    return log;
+}
+
+// 获取本地配置
+// fn read_params() -> Params {
+//     let mut path = "config.toml";
+//
+//     let args: Vec<String> = std::env::args().collect();
+//
+//     for arg in &args {
+//         if !arg.contains("--config") {
+//             continue;
+//         }
+//
+//         let p: Vec<&str> = arg.split("=").collect();
+//         path = p[1];
+//     }
+//
+//     println!("配置文件路径:{}", path);
+//     let params = Params::new(path).unwrap();
+//
+//     return params;
+// }
+
+// 获取本地配置- json 文件解析
+fn read_params_json() -> Params {
+    let mut path = "config.json";
+    let mut call_port = 5555;
+    // 运行模式 0.正常策略运行, 1.清理挂单及仓位
+    let mut run_mode = 0;
+    let mut r_id = "-1".to_string();
+
+    let args: Vec<String> = std::env::args().collect();
+
+    for arg in &args {
+        // Check for the --config argument and assign its value.
+        if arg.starts_with("--config=") {
+            let parts: Vec<&str> = arg.split('=').collect();
+            if parts.len() == 2 {
+                path = parts[1];
+            }
+        }
+
+        // Check for the --port argument and assign its value.
+        if arg.starts_with("--port=") {
+            let parts: Vec<&str> = arg.split('=').collect();
+            if parts.len() == 2 {
+                match u32::from_str(parts[1]) {
+                    Ok(num) => call_port = num,
+                    Err(_) => eprintln!("Invalid number for port: {}", parts[1]),
+                }
+            }
+        }
+
+        // Check for the --port argument and assign its value.
+        if arg.starts_with("--run_mode=") {
+            let parts: Vec<&str> = arg.split('=').collect();
+            if parts.len() == 2 {
+                match u32::from_str(parts[1]) {
+                    Ok(num) => run_mode = num,
+                    Err(_) => eprintln!("Invalid number for run_mode: {}", parts[1]),
+                }
+            }
+        }
+
+        //上报ID
+        if arg.starts_with("--r_id=") {
+            let parts: Vec<&str> = arg.split('=').collect();
+            if parts.len() == 2 {
+                r_id = parts[1].to_string();
+            } else {
+                error!("启动失败,回执单id参数格式设置错误 --check_id=xxx!");
+                panic!("启动失败,回执单id参数格式设置错误 --check_id=xxx!");
+            }
+        }
+    }
+    println!("通讯端口:{}, 配置文件路径:{}", call_port, path);
+    println!("请求指令携带参数:{:?}",args.clone());
+    let mut params = Params::new_json(path, call_port).unwrap();
+    if run_mode == 1 {
+        params.run_mode = 1;
+        if r_id == "-1" {
+            error!("启动失败,缺少回执单id参数!");
+            panic!("启动失败,缺少回执单id参数!");
+        }
+    }
+    params.r_id = r_id;
+    return params;
+}
+
+#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
+async fn main() {
+    // 日志级别配置
+    // let params = read_params();
+    let params = read_params_json();
+
+    // 日志级别配置
+    let _guard;
+    if params.run_mode == 1 {
+        _guard = clear_log_level_init(params.log_level.clone(), params.port.clone(), params.account_name.clone());
+    } else {
+        _guard = log_level_init(params.log_level.clone(), params.port.clone());
+    }
+
+    info!("--------------------------------程序开始执行-----------------------------");
+    info!("配置读取成功:{:?}。", params);
+    // 主进程控制
+    let running = Arc::new(AtomicBool::new(true));
+
+    // panic错误捕获,panic级别的错误直接退出
+    let account_name_clone = params.account_name.clone();
+    let panic_running = running.clone();
+    std::panic::set_hook(Box::new(move |panic_info| {
+        let msg = format!(
+            "account={}, type=panic, msg={:?}, location={:?}",
+            account_name_clone,
+            panic_info.to_string(),
+            panic_info.location()
+        );
+
+        // 生成并格式化完整的堆栈跟踪
+        let backtrace = Backtrace::new();
+        let stack_trace = format!("{:?}", backtrace);
+
+        // 一并打印堆栈跟踪
+        warn!("{}\nStack Trace:\n{}", msg, stack_trace);
+        panic_running.store(false, Ordering::Relaxed);
+    }));
+
+    // 中央控制器信息
+    let cci = CentralControlInfo::new();
+    let cci_arc = Arc::new(Mutex::new(cci));
+
+    // ws退出程序
+    let ws_running = Arc::new(AtomicBool::new(true));
+    if params.run_mode == 1 {
+        // core初始化动作
+        let mut core_arc = clear_core_libs::init(params.clone(), ws_running.clone(), running.clone(), cci_arc.clone()).await;
+        info!("开始执行清仓程序");
+        core_arc.exit(params.r_id).await;
+        info!("清仓程序执行完毕");
+        // 强制退出
+        std::process::exit(0);
+    } else {
+        // core初始化动作
+        let core_arc = core_libs::init(params.clone(), ws_running.clone(), running.clone(), cci_arc.clone()).await;
+        // 初始化中控服务
+        server::run_server(params.port.clone(), running.clone(), cci_arc.clone());
+        // ctrl c退出检查程序
+        control_c::exit_handler(running.clone());
+
+        // 每一秒检查一次程序是否结束
+        while running.load(Ordering::Relaxed) {
+            tokio::time::sleep(Duration::from_secs(1)).await;
+        }
+
+        info!("检测到退出信号!停止ws订阅……");
+        ws_running.store(false, Ordering::Relaxed);
+        tokio::time::sleep(Duration::from_secs(1)).await;
+
+        info!("等待清空仓位、订单(再次按control c可以立马结束)……");
+        let mut core = core_arc.lock().await;
+        core.exit().await;
+        info!("程序已退出!为以防万一,请再次检查仓位和订单!");
+        // 等两秒,等中控反应过来
+        tokio::time::sleep(Duration::from_secs(2)).await;
+        // 强制退出
+        std::process::exit(0);
+    }
+}

+ 126 - 0
src/server.rs

@@ -0,0 +1,126 @@
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::time::Duration;
+use actix_cors::Cors;
+use actix_web::{web, App, HttpResponse, HttpServer, Responder, post, get};
+use serde_json::json;
+use tokio::sync::Mutex;
+use tracing::{info};
+use global::cci::CentralControlInfo;
+
+// arcs
+#[derive(Clone)]
+struct Arcs {
+    running: Arc<AtomicBool>,
+    cci_cache_arc: Arc<Mutex<CentralControlInfo>>
+}
+
+// 账户相关数据
+#[get("/account")]
+async fn get_account(arcs: web::Data<Arcs>) -> impl Responder {
+    // --------------------------------数据解锁处理--------------------------------
+    let cci = arcs.cci_cache_arc.lock().await;
+
+    let rst = json!({
+        "now_balance": cci.now_balance,
+        "unrealized_pn_l": cci.unrealized_pn_l,
+        "pos": cci.pos,
+        "entry_price": cci.entry_price,
+        "now_price": cci.now_price
+    });
+
+    // --------------------------------回报--------------------------------
+    let json_string = serde_json::to_string(&rst).unwrap();
+    HttpResponse::Ok().content_type("application/json").body(json_string)
+}
+
+// 句柄 GET 请求
+// #[get("/predictor_state")]
+// async fn get_predictor_state(arcs: web::Data<Arcs>) -> impl Responder {
+//     // --------------------------------数据解锁处理--------------------------------
+//     let cci = arcs.cci_cache_arc.lock().await;
+//
+//     // 实时请求只回报最后1w条
+//     // 获取最后1w条数据
+//     let last_10k = if cci.predictor_state_vec.len() >= 10_000 {
+//         &cci.predictor_state_vec[cci.predictor_state_vec.len() - 10_000..] // 使用切片获取最后 10 万条
+//     } else {
+//         &cci.predictor_state_vec[..] // 如果长度不足 10 万条,则返回整个 Vec
+//     };
+//
+//     // --------------------------------回报--------------------------------
+//     let json_string = serde_json::to_string(&last_10k).unwrap();
+//     HttpResponse::Ok().content_type("application/json").body(json_string)
+// }
+
+// 句柄 POST 请求
+#[post("/exit")]
+async fn on_change(arcs: web::Data<Arcs>) -> impl Responder {
+    arcs.running.store(false, Ordering::Relaxed);
+    HttpResponse::Ok().body("程序已收到退出信号,将在清退仓位后退出。".to_string())
+}
+
+pub fn run_cci_cache(cci_arc: Arc<Mutex<CentralControlInfo>>) -> Arc<Mutex<CentralControlInfo>> {
+    // cci缓存,防止中控因等待时间过长杀死rust
+    let cci_cache = CentralControlInfo::new();
+    let cci_cache_arc = Arc::new(Mutex::new(cci_cache));
+    let cci_cache_arc_clone = cci_cache_arc.clone();
+    tokio::spawn(async move {
+        loop {
+            tokio::time::sleep(Duration::from_secs(1)).await;
+
+            let cci = cci_arc.lock().await;
+            {
+                let mut cci_cache = cci_cache_arc_clone.lock().await;
+                cci_cache.now_price = cci.now_price;
+                cci_cache.pos = cci.pos;
+                cci_cache.unrealized_pn_l = cci.unrealized_pn_l;
+                cci_cache.now_balance = cci.now_balance;
+                cci_cache.entry_price = cci.entry_price;
+                cci_cache.predictor_state_vec = cci.predictor_state_vec.clone();
+            }
+        }
+    });
+
+    return cci_cache_arc;
+}
+
+pub fn run_server(port: u32, running: Arc<AtomicBool>, cci_arc: Arc<Mutex<CentralControlInfo>>) {
+    let addr = format!("0.0.0.0:{}", port);
+    info!("中控绑定地址:{}", addr);
+
+    // 启动server
+    let arcs = Arcs {
+        running: running.clone(),
+        cci_cache_arc: run_cci_cache(cci_arc),
+    };
+
+    let server_fut = HttpServer::new(move || {
+        let arcs_clone = arcs.clone();
+
+        // 配置 CORS
+        let cors = Cors::permissive()
+            .allow_any_origin()
+            .allow_any_header()
+            .allow_any_method()
+            .max_age(3600); // 设置预检请求的缓存时间
+
+        App::new()
+            .wrap(cors)
+            .app_data(web::Data::new(arcs_clone))
+            .service(get_account)
+            // .service(get_predictor_state)
+            .service(on_change)
+    })
+    .bind(addr)
+    .expect("Bind port error")
+    .run();
+
+    info!("中控服务已运行。");
+
+    let r = running.clone();
+    tokio::spawn(async move {
+        server_fut.await.expect("error running the server");
+        r.store(false, Ordering::Relaxed);
+    });
+}

+ 5 - 0
standard/.gitignore

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

+ 23 - 0
standard/Cargo.toml

@@ -0,0 +1,23 @@
+[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 = { version = "1.32.0", features = ["maths"] }
+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"
+lazy_static = "1.4.0"

+ 15 - 0
standard/README.md

@@ -0,0 +1,15 @@
+## 声明
+
+
+## 项目结构解析
+
+```
+|
+├─ main                                 // 系统入口
+│
+├─ exchanges                            // 交易所层(网络层)
+│
+├─ standard                             // 标准化层(中间件)
+│
+└─ strategy                             // 策略层(主逻辑、风控等)
+```

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

+ 675 - 0
standard/src/binance_swap.rs

@@ -0,0 +1,675 @@
+use std::collections::BTreeMap;
+use std::io::{Error, ErrorKind};
+use std::result::Result;
+use std::str::FromStr;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::{json, Value};
+use tokio::spawn;
+use tokio::sync::mpsc::Sender;
+use tokio::time::Instant;
+use tracing::{error, info};
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, utils, PositionModeEnum, Record};
+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,
+        };
+
+        // 修改持仓模式
+        let mode_result = binance_swap.set_dual_mode("", false).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("Binance:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("Binance:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 设置持仓杠杆
+        let lever_rate_result = binance_swap.set_dual_leverage("1").await;
+        match lever_rate_result {
+            Ok(ok) => {
+                info!("Binance:设置持仓杠杆成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("Binance:设置持仓杠杆失败!{:?}", error)
+            }
+        }
+        // 获取市场信息
+        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.multiplier;
+        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: Decimal::from_i64(res_data_json["time"].as_i64().unwrap()).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),
+                open_interest: Default::default(),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_record(&mut self, _interval: String) -> Result<Vec<Record>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "binance_usdt_swap:该交易所方法未实现".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: Decimal::from_i64(res_data_json["time"].as_i64().unwrap()).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),
+                open_interest: Default::default(),
+            };
+            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 min_notional_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "MIN_NOTIONAL").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(min_notional_filter["notional"].as_str().unwrap()).unwrap(),
+                        max_notional: Decimal::from_str(price_filter["maxPrice"].as_str().unwrap()).unwrap(),
+                        multiplier: 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(),
+                        multiplier: 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 mut params = json!({"symbol": symbol_format.clone()});
+
+        if order_id != "" { params["orderId"] = json!(order_id) } else { params["origClientOrderId"] = json!(custom_id) }
+        let res_data = self.request.get_order(params).await;
+        if res_data.code == 200 {
+            let res_data_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 params = json!({
+            "symbol":symbol_format.clone()
+        });
+        let res_data = self.request.get_open_orders(params).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> {
+        self.take_order_symbol(self.symbol.clone(), self.market.multiplier, 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.clone(), "");
+        let mut params = json!({
+            "newClientOrderId": custom_id.to_string(),
+            "symbol": symbol_format,
+        });
+        let size = amount / ct_val;
+        params["quantity"] = json!(size);
+        if price.eq(&Decimal::ZERO) {
+            params["type"] = json!("MARKET");
+        } else {
+            params["price"] = json!(price.to_string());
+            params["type"] = json!("LIMIT");
+            params["timeInForce"] = json!("GTC");
+        };
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("BUY");
+                // params["positionSide"] = json!("LONG");
+            }
+            "pd" => {
+                params["side"] = json!("SELL");
+                params["reduceOnly"] = json!(true);
+                // params["positionSide"] = json!("LONG");
+            }
+            "kk" => {
+                params["side"] = json!("SELL");
+                // params["positionSide"] = json!("SHORT");
+            }
+            "pk" => {
+                params["side"] = json!("BUY");
+                params["reduceOnly"] = json!(true);
+                // params["positionSide"] = json!("SHORT");
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data;
+            let id = res_data_json["orderId"].to_string();
+            let custom_id = res_data_json["clientOrderId"].as_str().unwrap().to_string();
+            let price = Decimal::from_str(res_data_json["price"].as_str().unwrap()).unwrap();
+            let amount = Decimal::from_str(res_data_json["origQty"].as_str().unwrap()).unwrap();
+            let deal_amount = Decimal::from_str(res_data_json["executedQty"].as_str().unwrap()).unwrap();
+            let avg_price = Decimal::from_str(res_data_json["avgPrice"].as_str().unwrap()).unwrap();
+            let result = Order {
+                id,
+                custom_id,
+                price,
+                amount,
+                deal_amount,
+                avg_price,
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("387 binance_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 = utils::format_symbol(self.symbol.clone(), "");
+        let mut params = json!({
+            "symbol": symbol_format.clone(),
+        });
+        if order_id != "" { params["orderId"] = json!(order_id) }
+        if custom_id != "" { params["origClientOrderId"] = json!(custom_id) }
+        // println!("{}", params);
+        let res_data = self.request.cancel_order(params).await;
+        if res_data.code == 200 {
+            let res_data_json = &res_data.data;
+            let cum_quote = Decimal::from_str(res_data_json["cumQuote"].as_str().unwrap()).unwrap();
+            let id = res_data_json["orderId"].to_string();
+            let custom_id = res_data_json["clientOrderId"].as_str().unwrap().to_string();
+            let price = Decimal::from_str(res_data_json["price"].as_str().unwrap()).unwrap();
+            let amount = Decimal::from_str(res_data_json["origQty"].as_str().unwrap()).unwrap();
+            let deal_amount = Decimal::from_str(res_data_json["executedQty"].as_str().unwrap()).unwrap();
+            let avg_price = if cum_quote > Decimal::ZERO { deal_amount / cum_quote } else { Decimal::ZERO };
+            let result = Order {
+                id,
+                custom_id,
+                price,
+                amount,
+                deal_amount,
+                avg_price,
+                status: "REMOVE".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("419 binance_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_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format.clone(),
+        });
+        let res_data = self.request.cancel_order_all(params).await;
+        println!("{:?}", res_data);
+        if res_data.code == 200 {
+            let result = vec![Order {
+                id: "".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: "limit".to_string(),
+                trace_stack: TraceStack::new(0, Instant::now()).on_special("484 trace_stack".to_string()),
+            }];
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.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> {
+        let res_data = self.request.change_pos_side(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_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+            "leverage" : leverage,
+        });
+        let res_data = self.request.setting_dual_leverage(params).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, "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) {
+        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["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(),
+    }
+}

+ 216 - 0
standard/src/binance_swap_handle.rs

@@ -0,0 +1,216 @@
+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, Depth, Order, OrderBook, Position, PositionModeEnum, Record, SpecialOrder, Trade, utils};
+
+
+// 处理账号信息
+pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account {
+    let symbol_format = utils::format_symbol(symbol.clone(), "");
+    let res_data_json = res_data.data["a"]["B"].as_array().unwrap();
+
+    let balance_info = res_data_json.iter().find(|&item| symbol_format.ends_with(item["a"].as_str().unwrap()));
+    match balance_info {
+        None => {
+            error!("Gate:格式化账号信息错误!\nformat_account_info: data={:?}", res_data_json);
+            panic!("Gate:格式化账号信息错误!\nformat_account_info: data={:?}", res_data_json)
+        }
+        Some(value) => {
+            let coin = value["a"].as_str().unwrap().to_string();
+            let balance = Decimal::from_str(&value["wb"].as_str().unwrap()).unwrap();
+            let available_balance = Decimal::from_str(&value["cw"].as_str().unwrap()).unwrap();
+            let frozen_balance = balance - available_balance;
+            Account {
+                coin,
+                balance,
+                available_balance,
+                frozen_balance,
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
+            }
+        }
+    }
+}
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, mul: &Decimal) -> Vec<Position> {
+    let res_data_json = res_data.data["a"]["P"].as_array().unwrap();
+    res_data_json.iter().map(|item| { format_position_item(item, mul) }).collect()
+}
+pub fn format_position_item(position: &Value, ct_val: &Decimal) -> Position {
+    let mut position_mode = match position["ps"].as_str().unwrap_or("") {
+        "BOTH" => PositionModeEnum::Both,
+        "LONG" => PositionModeEnum::Long,
+        "SHORT" => PositionModeEnum::Short,
+        _ => {
+            error!("Gate:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("Gate:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size = Decimal::from_str(&position["pa"].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["s"].as_str().unwrap().to_string(),
+        margin_level: dec!(-1),
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(&position["ep"].as_str().unwrap()).unwrap(),
+        profit: Decimal::from_str(&position["up"].as_str().unwrap()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(&position["iw"].as_str().unwrap()).unwrap(),
+    }
+}
+
+// 处理order信息
+pub fn handle_order(res_data: &ResponseData, ct_val: &Decimal) -> SpecialOrder {
+    let res_data_json = res_data.data["o"].clone();
+    let order_info = vec![format_order_item(res_data_json.clone(), ct_val)];
+
+
+    SpecialOrder {
+        name: res_data.label.clone(),
+        order: order_info,
+    }
+}
+
+pub fn format_order_item(order: Value, ct_val: &Decimal) -> Order {
+    let id = order["i"].to_string();
+    let custom_id = order["c"].as_str().unwrap().to_string();
+    let price = Decimal::from_str(order["p"].as_str().unwrap()).unwrap();
+    let amount = Decimal::from_str(order["q"].as_str().unwrap()).unwrap() * ct_val;
+    let deal_amount = Decimal::from_str(order["z"].as_str().unwrap()).unwrap();
+    let avg_price = Decimal::from_str(order["ap"].as_str().unwrap()).unwrap();
+
+    let status = order["X"].as_str().unwrap();
+
+    let custom_status = if vec!["FILLED", "CANCELED", "REJECTED", "EXPIRED"].contains(&status) { "REMOVE".to_string() } else if ["NEW", "PARTIALLY_FILLED"].contains(&status) { "NEW".to_string() } else {
+        error!("Gate:格式化订单状态错误!\nformat_order_item:order={:?}", order);
+        panic!("Gate:格式化订单状态错误!\nformat_order_item:order={:?}", order)
+    };
+    let rst_order = Order {
+        id,
+        custom_id,
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: "limit".to_string(),
+        trace_stack: TraceStack::new(0, Instant::now()).on_special("115 binance_handle".to_string()),
+    };
+
+    return rst_order;
+}
+
+// 处理特殊Ticket信息
+pub fn handle_book_ticker(res_data: &ResponseData, mul: &Decimal) -> Depth {
+    let bid_price = Decimal::from_str(res_data.data["b"].as_str().unwrap()).unwrap();
+    let bid_amount = Decimal::from_str(res_data.data["B"].as_str().unwrap()).unwrap();
+    let ask_price = Decimal::from_str(res_data.data["a"].as_str().unwrap()).unwrap();
+    let ask_amount = Decimal::from_str(res_data.data["A"].as_str().unwrap()).unwrap();
+    // let u = Decimal::from_u64((*res_data).data["u"].as_u64().unwrap()).unwrap();
+    let t = Decimal::from_i64(res_data.data["T"].as_i64().unwrap()).unwrap();
+    let s = res_data.data["s"].as_str().unwrap().replace("USDT", "_USDT");
+
+    let asks = vec![
+        OrderBook {
+            price: ask_price,
+            size: ask_amount,
+            value: ask_price * ask_amount * mul,
+        }
+    ];
+    let bids = vec![
+        OrderBook {
+            price: bid_price,
+            size: bid_amount,
+            value: bid_price * bid_amount * mul,
+        }
+    ];
+
+    Depth {
+        time: t,
+        symbol: s.to_string(),
+        asks,
+        bids,
+    }
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: Value) -> Vec<OrderBook> {
+    let mut depth_items: Vec<OrderBook> = vec![];
+    for value in value.as_array().unwrap() {
+        let price = Decimal::from_str(value[0].as_str().unwrap()).unwrap();
+        let size = Decimal::from_str(value[1].as_str().unwrap()).unwrap();
+        let value = price * size;
+
+        depth_items.push(OrderBook {
+            price,
+            size,
+            value,
+        })
+    }
+    return depth_items;
+}
+
+pub fn format_trade_items(response: &ResponseData) -> Vec<Trade> {
+    let data = response.data.clone();
+
+    let id = data["a"].as_i64().unwrap().to_string();
+    let time = Decimal::from_i64(data["T"].as_i64().unwrap()).unwrap();
+    let is_sell = data["m"].as_bool().unwrap(); // 买方是否是做市方。如true,则此次成交是一个主动卖出单,否则是一个主动买入单。
+    let mut size = Decimal::from_str(data["q"].as_str().unwrap().to_string().as_str()).unwrap();
+    if is_sell {
+        size = -size
+    }
+    let price = Decimal::from_str(data["p"].as_str().unwrap().to_string().as_str()).unwrap();
+    let symbol = data["s"].as_str().unwrap().to_string().replace("USDT", "_USDT");
+    let value = (price * size).abs();
+
+    vec![Trade {
+        id,
+        time,
+        size,
+        price,
+        value,
+        symbol,
+    }]
+}
+
+pub fn handle_records(value: &Value) -> Vec<Record> {
+    let data = value["k"].clone();
+    let time = Decimal::from_i64(data["t"].as_i64().unwrap()).unwrap();
+
+    let open = Decimal::from_str(data["o"].as_str().unwrap().to_string().as_str()).unwrap();
+    let high = Decimal::from_str(data["h"].as_str().unwrap().to_string().as_str()).unwrap();
+    let low = Decimal::from_str(data["l"].as_str().unwrap().to_string().as_str()).unwrap();
+    let close = Decimal::from_str(data["c"].as_str().unwrap().to_string().as_str()).unwrap();
+    let volume = Decimal::from_str(data["q"].as_str().unwrap().to_string().as_str()).unwrap();
+    let symbol = data["s"].as_str().unwrap().to_string().replace("USDT", "_USDT");
+
+    vec![Record {
+        time,
+        open,
+        high,
+        low,
+        close,
+        volume,
+        symbol,
+    }]
+}

+ 259 - 0
standard/src/bingx_swap.rs

@@ -0,0 +1,259 @@
+// use std::collections::{BTreeMap};
+// use std::io::{Error, ErrorKind};
+// use std::str::FromStr;
+// use tokio::sync::mpsc::Sender;
+// use async_trait::async_trait;
+// use rust_decimal::{Decimal, MathematicalOps};
+// use serde_json::{json, Value};
+// use tracing::{error, info};
+// use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, utils};
+// use exchanges::bingx_swap_rest::BingxSwapRest;
+//
+// #[allow(dead_code)]
+// #[derive(Clone)]
+// pub struct BingxSwap {
+//     exchange: ExchangeEnum,
+//     symbol: String,
+//     is_colo: bool,
+//     params: BTreeMap<String, String>,
+//     request: BingxSwapRest,
+//     market: Market,
+//     order_sender: Sender<Order>,
+//     error_sender: Sender<Error>,
+// }
+//
+// impl BingxSwap {
+//     pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BingxSwap {
+//         let market = Market::new();
+//         let mut bingx_swap = BingxSwap {
+//             exchange: ExchangeEnum::BingxSwap,
+//             symbol: symbol.to_uppercase(),
+//             is_colo,
+//             params: params.clone(),
+//             request: BingxSwapRest::new(is_colo, params.clone()),
+//             market,
+//             order_sender,
+//             error_sender,
+//         };
+//
+//         // 修改持仓模式
+//         let symbol_array: Vec<&str> = symbol.split("_").collect();
+//         let mode_result = bingx_swap.set_dual_mode(symbol_array[1], true).await;
+//         match mode_result {
+//             Ok(ok) => {
+//                 info!("Bingx:设置持仓模式成功!{:?}", ok);
+//             }
+//             Err(error) => {
+//                 error!("Bingx:设置持仓模式失败!mode_result={}", error)
+//             }
+//         }
+//         // 获取市场信息
+//         bingx_swap.market = BingxSwap::get_market(&mut bingx_swap).await.unwrap_or(bingx_swap.market);
+//         return bingx_swap;
+//     }
+// }
+//
+// #[async_trait]
+// impl Platform for BingxSwap {
+//     // 克隆方法
+//     fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+//     // 获取交易所模式
+//     fn get_self_exchange(&self) -> ExchangeEnum {
+//         ExchangeEnum::BingxSwap
+//     }
+//     // 获取交易对
+//     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> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//     // 获取账号信息
+//     async fn get_account(&mut self) -> Result<Account, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     // 获取持仓信息
+//     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//     // 获取所有持仓
+//     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//     // 获取市场行情
+//     async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn get_market(&mut self) -> Result<Market, Error> {
+//         let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+//         let params = json!({});
+//         let res_data = self.request.get_market(params).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!("bingx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+//                     Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//                 }
+//                 Some(value) => {
+//                     let name = value["symbol"].as_str().unwrap();
+//                     let name_array: Vec<&str> = name.split("-").collect();
+//                     let price_precision = Decimal::from_str(&value["pricePrecision"].to_string()).unwrap();
+//                     let tick_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * price_precision);
+//                     let amount_precision = Decimal::from_str(&value["quantityPrecision"].to_string()).unwrap();
+//                     let amount_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * amount_precision);
+//                     let min_qty = Decimal::from_str(&value["tradeMinUSDT"].to_string()).unwrap();
+//                     let max_qty = Decimal::NEGATIVE_ONE;
+//                     let ct_val = Decimal::ONE;
+//
+//                     let result = Market {
+//                         symbol: name_array.join("_"),
+//                         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: min_qty,
+//                         max_notional: max_qty,
+//                         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 params = json!({
+//             "symbol": symbol_format.clone()
+//         });
+//         let res_data = self.request.get_market(params).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!("bingx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+//                     Err(Error::new(ErrorKind::Other, res_data.to_string()))
+//                 }
+//                 Some(value) => {
+//                     let name = value["symbol"].as_str().unwrap();
+//                     let name_array: Vec<&str> = name.split("-").collect();
+//                     let price_precision = Decimal::from_str(&value["pricePrecision"].to_string()).unwrap();
+//                     let tick_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * price_precision);
+//                     let amount_precision = Decimal::from_str(&value["quantityPrecision"].to_string()).unwrap();
+//                     let amount_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * amount_precision);
+//                     let min_qty = Decimal::from_str(&value["tradeMinUSDT"].to_string()).unwrap();
+//                     let max_qty = Decimal::NEGATIVE_ONE;
+//                     let ct_val = Decimal::ONE;
+//
+//                     let result = Market {
+//                         symbol: name_array.join("_"),
+//                         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: min_qty,
+//                         max_notional: max_qty,
+//                         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> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//     // 获取订单列表
+//     async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".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, "bingx_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, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     // 撤销订单
+//     async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//     // 批量撤销订单
+//     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_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, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     // 设置持仓模式
+//     async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     // 更新双持仓模式下杠杆
+//     async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+//
+//     async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "bingx:该交易所方法未实现".to_string())) }
+//
+//     // 交易账户互转
+//     async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+//         Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+//     }
+// }

+ 105 - 0
standard/src/bingx_swap_handle.rs

@@ -0,0 +1,105 @@
+// use std::str::FromStr;
+// use chrono::Utc;
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::FromPrimitive;
+// use serde_json::Value;
+// use exchanges::response_base::ResponseData;
+// use crate::{OrderBook, Trade, Record};
+// // use crate::{Account, OrderBook, Order, Position, SpecialOrder, Trade, Record};
+//
+// // 处理账号信息
+// // pub fn handle_account_info(_res_data: &ResponseData, _symbol: &String) -> Account {
+// //     Account::new()
+// // }
+// //
+// // pub fn format_account_info(_data: &Vec<Value>, _symbol: &String) -> Account {
+// //     Account::new()
+// // }
+// //
+// // // 处理position信息
+// // pub fn handle_position(_res_data: &ResponseData, _ct_val: &Decimal) -> Vec<Position> {
+// //     vec![Position::new()]
+// // }
+// //
+// // pub fn format_position_item(_position: &Value, _ct_val: &Decimal) -> Position {
+// //     Position::new()
+// // }
+// //
+// // // 处理order信息
+// // pub fn handle_order(_res_data: &ResponseData, _ct_val: Decimal) -> SpecialOrder {
+// //     SpecialOrder::new()
+// // }
+// //
+// // pub fn format_order_item(_order: Value, _ct_val: Decimal) -> Order {
+// //     Order::new()
+// // }
+// // 处理特殊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).tag.clone(),
+// //         depth: depth_info,
+// //         ticker: ticker_info,
+// //         t,
+// //         create_at,
+// //     }
+// // }
+//
+// pub fn handle_records(value: &Value) -> Vec<Record> {
+//     let mut records = vec![];
+//     let symbol = value["s"].as_str().unwrap().replace("-", "_");
+//
+//     for record_value in value["data"].as_array().unwrap() {
+//         records.push(Record {
+//             time: Decimal::from_i64(Utc::now().timestamp_millis()).unwrap(),
+//             open: Decimal::from_str(record_value["o"].as_str().unwrap()).unwrap(),
+//             high: Decimal::from_str(record_value["h"].as_str().unwrap()).unwrap(),
+//             low: Decimal::from_str(record_value["l"].as_str().unwrap()).unwrap(),
+//             close: Decimal::from_str(record_value["c"].as_str().unwrap()).unwrap(),
+//             volume: Decimal::from_str(record_value["v"].as_str().unwrap()).unwrap(),
+//             symbol: symbol.clone(),
+//         });
+//     }
+//
+//     return records;
+// }
+//
+// pub fn format_depth_items(value: &Value) -> Vec<OrderBook> {
+//     let mut depth_items: Vec<OrderBook> = vec![];
+//     for value in value.as_array().unwrap() {
+//         depth_items.push(OrderBook {
+//             price: Decimal::from_str(value["p"].as_str().unwrap()).unwrap(),
+//             amount: Decimal::from_f64(value["s"].as_f64().unwrap()).unwrap(),
+//         })
+//     }
+//     return depth_items;
+// }
+//
+// pub fn format_trade_items(res_data: &ResponseData) -> Vec<Trade> {
+//     let result = res_data.data["data"].as_array().unwrap();
+//     let mut trades = vec![];
+//
+//     for item in result {
+//         let side = item["m"] == true;
+//         let size = Decimal::from_str(item["q"].as_str().unwrap()).unwrap();
+//         trades.push(Trade {
+//             id: item["T"].to_string(),
+//             time: Decimal::from_i64(item["T"].as_i64().unwrap()).unwrap(),
+//             size: if side { -size } else { size },
+//             price: Decimal::from_str(item["p"].as_str().unwrap().to_string().as_str()).unwrap(),
+//             symbol: item["s"].as_str().unwrap().to_string().replace("-", "_"),
+//         })
+//     }
+//
+//     return trades;
+// }

+ 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.tag,
+//         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.tag)
+// }
+//
+// pub fn format_special_ticker(data: serde_json::Value, tag: 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: tag,
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at,
+//     }
+// }

+ 791 - 0
standard/src/bitget_swap.rs

@@ -0,0 +1,791 @@
+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_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, Record, 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("", false).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("BitgetSwap:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("BitgetSwap:设置持仓模式失败!{:?}", error)
+            }
+        }
+        // 设置持仓杠杆
+        let lever_rate_result = bitget_swap.set_dual_leverage("1").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"].as_str().unwrap().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> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "productType": "USDT-FUTURES",
+            "marginCoin": "USDT",
+            "symbol": symbol_format
+        });
+        let response = self.request.get_single_position(params).await;
+        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 mut 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();
+
+            let side = position_json["holdSide"].as_str().unwrap();
+            match position_mode {
+                PositionModeEnum::Both => {
+                    position_mode = match side {
+                        "long" => PositionModeEnum::Long,
+                        "short" => PositionModeEnum::Short,
+                        _ => { PositionModeEnum::Both }
+                    }
+                }
+                _ => {}
+            }
+
+            positions.push(Position {
+                symbol,
+                margin_level,
+                amount,
+                frozen_amount,
+                price,
+                profit,
+                position_mode,
+                margin,
+            });
+        }
+
+        Ok(positions)
+    }
+
+    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;
+
+        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 mut 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();
+
+            let side = position_json["holdSide"].as_str().unwrap();
+            match position_mode {
+                PositionModeEnum::Both => {
+                    position_mode = match side {
+                        "long" => PositionModeEnum::Long,
+                        "short" => PositionModeEnum::Short,
+                        _ => { PositionModeEnum::Both }
+                    }
+                }
+                _ => {}
+            }
+
+            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_record(&mut self, interval: String) -> Result<Vec<Record>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+
+        let params = json!({
+            "symbol": symbol_format.clone(),
+            "productType": "USDT-FUTURES",
+            "granularity": format!("{}m",interval),
+            "limit": "3"
+        });
+
+        let res_data = self.request.get_market_candles(params).await;
+        if res_data.code == 200 {
+            let mut records: Vec<Record> = vec![];
+            for record_value in res_data.data.as_array().unwrap() {
+                records.push(Record {
+                    time: Decimal::from_str(record_value[0].as_str().unwrap()).unwrap(),
+                    open: Decimal::from_str(record_value[1].as_str().unwrap()).unwrap(),
+                    high: Decimal::from_str(record_value[2].as_str().unwrap()).unwrap(),
+                    low: Decimal::from_str(record_value[3].as_str().unwrap()).unwrap(),
+                    close: Decimal::from_str(record_value[4].as_str().unwrap()).unwrap(),
+                    volume: Decimal::from_str(record_value[5].as_str().unwrap()).unwrap(),
+                    symbol: symbol_format.clone(),
+                });
+            }
+
+            records.reverse();
+            Ok(records)
+        } 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_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);
+            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(),
+                open_interest: Default::default(),
+            };
+            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();
+
+            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::from_str(market_info["minTradeNum"].as_str().unwrap()).unwrap();
+            let max_qty = Decimal::NEGATIVE_ONE;
+            let min_notional = Decimal::from_str(market_info["minTradeUSDT"].as_str().unwrap()).unwrap();
+            let max_notional = Decimal::NEGATIVE_ONE;
+            let multiplier = Decimal::from_str(&market_info["sizeMultiplier"].as_str().unwrap()).unwrap();
+            // let multiplier = 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,
+                max_notional,
+                multiplier,
+            };
+            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 multiplier = self.market.multiplier;
+        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, multiplier);
+            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 multiplier = self.market.multiplier;
+        // 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(), multiplier)).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 multiplier = self.market.multiplier;
+        return self.take_order_symbol(self.symbol.clone(), multiplier, custom_id, origin_side, price, amount).await;
+    }
+
+    async fn take_order_symbol(&mut self, symbol: String, multiplier: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(symbol, "");
+        let mut params = json!({
+            "symbol": symbol_format,
+            "clientOid": custom_id,
+            "productType": "USDT-FUTURES",
+            "marginMode": "crossed",
+            "marginCoin": "USDT",
+            "size": amount.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");
+            if amount % multiplier != dec!(0) {
+                params["size"] = json!(((amount / multiplier).ceil() * multiplier).to_string());
+            }
+        };
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+                params["reduceOnly"] = json!("NO");
+            }
+            "pd" => {
+                params["side"] = json!("sell");
+                params["reduceOnly"] = json!("YES");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+                params["reduceOnly"] = json!("NO");
+            }
+            "pk" => {
+                params["side"] = json!("buy");
+                params["reduceOnly"] = json!("YES");
+            }
+            _ => { 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("500 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("530 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;
+        if response.code == 200 {
+            let mut result = vec![];
+
+            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 symbol = order["symbol"].as_str().unwrap().to_string();
+                    let order_id = order["orderId"].as_str().unwrap().to_string();
+                    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("567 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 symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({
+            "symbol": symbol_format,
+            "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) => {
+                        error!(?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(_) => {
+                                // 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, _multiplier: 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;
+    let deal_amount = base_volume;
+    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: 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("791 bitget_swap".to_string()),
+    }
+}

+ 268 - 0
standard/src/bitget_swap_handle.rs

@@ -0,0 +1,268 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::Value;
+use tokio::time::Instant;
+use tracing::error;
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, OrderBook, Order, Position, PositionModeEnum, SpecialOrder, Depth, Trade, Record};
+
+// 处理账号信息
+pub fn handle_account_info(response: &ResponseData, _symbol: &String) -> Account {
+    let mut rst = Account::new();
+    let data_info = response.data["data"].clone();
+    for data in data_info.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, multiplier: &Decimal) -> SpecialOrder {
+    let data_info = res_data.data["data"].clone();
+    let res_data_json = data_info.as_array().unwrap();
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), multiplier.clone()));
+    }
+    SpecialOrder {
+        name: res_data.label.clone(),
+        order: order_info,
+    }
+}
+
+// 处理订单信息
+pub fn format_order_item(order: Value, _multiplier: 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;
+    let deal_amount = acc_base_volume;
+    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, mul: &Decimal) -> Vec<OrderBook> {
+    let mut depth_items: Vec<OrderBook> = vec![];
+    for value in value.as_array().unwrap() {
+        let price = Decimal::from_str(value[0].as_str().unwrap()).unwrap();
+        let size = Decimal::from_str(value[1].as_str().unwrap()).unwrap();
+        depth_items.push(OrderBook {
+            price,
+            size,
+            value: price * size * mul,
+        })
+    }
+    return depth_items;
+}
+
+// 处理position信息
+pub fn handle_position(res_data: &ResponseData, multiplier: &Decimal) -> Vec<Position> {
+    let data_info = res_data.data["data"].clone();
+    let data_info_json = data_info.as_array().unwrap();
+    data_info_json.iter().map(|item| { format_position_item(item, multiplier) }).collect()
+}
+
+pub fn format_position_item(position_json: &Value, _multiplier: &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();
+    let frozen_amount = Decimal::from_str(position_json["frozen"].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 mut 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();
+
+    let side = position_json["holdSide"].as_str().unwrap();
+    match position_mode {
+        PositionModeEnum::Both => {
+            position_mode = match side {
+                "long" => PositionModeEnum::Long,
+                "short" => PositionModeEnum::Short,
+                _ => { PositionModeEnum::Both }
+            }
+        }
+        _ => {}
+    }
+
+    Position {
+        symbol,
+        margin_level,
+        amount,
+        frozen_amount,
+        price,
+        profit,
+        position_mode,
+        margin,
+    }
+}
+
+pub fn format_trade_items(response: &ResponseData) -> Vec<Trade> {
+    let data_info = response.data["data"].clone();
+    let result = data_info.as_array().unwrap();
+    let mut trades = vec![];
+
+    for item in result {
+        // 因为gate的量都是张数,所以要进行真实交易量处理
+        let mut size = Decimal::from_str(item["size"].as_str().unwrap()).unwrap();
+        let price = Decimal::from_str(item["price"].as_str().unwrap().to_string().as_str()).unwrap();
+        let side = item["side"].as_str().unwrap().to_string();
+        size = match side.as_str() {
+            "buy" => {
+                size
+            }
+            "sell" => {
+                -size
+            }
+            _ => {
+                error!("{}", item.to_string());
+                panic!("Bitget trade error side(bitget_swap_handle_156)")
+            }
+        };
+        let value = (size * price).abs();
+
+        trades.push(Trade {
+            id: item["tradeId"].as_str().unwrap().to_string(),
+            time: Decimal::from_str(item["ts"].as_str().unwrap()).unwrap(),
+            size,
+            price,
+            value,
+            symbol: response.data["arg"]["instId"].as_str().unwrap().replace("USDT", "_USDT"),
+        })
+    }
+
+    return trades;
+}
+pub fn handle_book_ticker(res_data: &ResponseData, mul: &Decimal) -> Depth {
+    let data_info = res_data.data["data"].clone();
+    let asks = format_depth_items(data_info[0]["asks"].clone(), mul);
+    let bids = format_depth_items(data_info[0]["bids"].clone(), mul);
+    let t = Decimal::from_i64(res_data.reach_time).unwrap();
+    let s = res_data.data["arg"]["instId"].as_str().unwrap().replace("USDT", "_USDT");
+
+    Depth {
+        time: t,
+        symbol: s.to_string(),
+        asks,
+        bids,
+    }
+}
+
+pub fn handle_records(value: &Value) -> Vec<Record> {
+    let data_info = value["data"].clone();
+    let mut records = vec![];
+    for record_value in data_info.as_array().unwrap() {
+        records.push(Record {
+            time: Decimal::from_str(record_value[0].as_str().unwrap()).unwrap(),
+            open: Decimal::from_str(record_value[1].as_str().unwrap()).unwrap(),
+            high: Decimal::from_str(record_value[2].as_str().unwrap()).unwrap(),
+            low: Decimal::from_str(record_value[3].as_str().unwrap()).unwrap(),
+            close: Decimal::from_str(record_value[4].as_str().unwrap()).unwrap(),
+            volume: Decimal::from_str(record_value[5].as_str().unwrap()).unwrap(),
+            symbol: value["arg"]["instId"].as_str().unwrap().replace("USDT", "_USDT"),
+        });
+    }
+
+    return records;
+}
+
+// 处理特殊深度数据
+// 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,
+//     }
+// }

Some files were not shown because too many files changed in this diff