Browse Source

合并dev新架构

gepangpang 1 year ago
parent
commit
dc62dbeb53
85 changed files with 5766 additions and 3199 deletions
  1. 2 1
      .gitignore
  2. 2 1
      Cargo.toml
  3. 3 0
      derive/.gitignore
  4. 21 0
      derive/Cargo.toml
  5. 92 0
      derive/src/binance_swap_export.rs
  6. 78 0
      derive/src/bitget_spot_export.rs
  7. 46 0
      derive/src/export_excel.rs
  8. 94 0
      derive/src/gate_swap_export.rs
  9. 104 0
      derive/src/kucoin_spot_export.rs
  10. 110 0
      derive/src/kucoin_swap_export.rs
  11. 14 0
      derive/src/lib.rs
  12. 120 0
      derive/src/okx_swap_export.rs
  13. 19 0
      derive/tests/binance_swap_export_test.rs
  14. 18 0
      derive/tests/bitget_spot_export_test.rs
  15. 77 0
      derive/tests/export_excel_test.rs
  16. 19 0
      derive/tests/gate_swap_export_test.rs
  17. 18 0
      derive/tests/kucoin_spot_export_test.rs
  18. 18 0
      derive/tests/kucoin_swap_export_test.rs
  19. 18 0
      derive/tests/okx_swap_export_test.rs
  20. 12 3
      exchanges/Cargo.toml
  21. 131 266
      exchanges/src/binance_spot_ws.rs
  22. 24 0
      exchanges/src/binance_swap_rest.rs
  23. 145 245
      exchanges/src/binance_swap_ws.rs
  24. 43 9
      exchanges/src/bitget_spot_rest.rs
  25. 322 303
      exchanges/src/bitget_spot_ws.rs
  26. 8 3
      exchanges/src/gate_swap_rest.rs
  27. 296 279
      exchanges/src/gate_swap_ws.rs
  28. 36 0
      exchanges/src/kucoin_spot_rest.rs
  29. 179 297
      exchanges/src/kucoin_spot_ws.rs
  30. 37 1
      exchanges/src/kucoin_swap_rest.rs
  31. 189 301
      exchanges/src/kucoin_swap_ws.rs
  32. 107 6
      exchanges/src/okx_swap_rest.rs
  33. 359 264
      exchanges/src/okx_swap_ws.rs
  34. 53 1
      exchanges/src/proxy.rs
  35. 353 0
      exchanges/src/socket_tool.rs
  36. 1 1
      exchanges/src/utils.rs
  37. 68 26
      exchanges/tests/binance_spot_test.rs
  38. 153 26
      exchanges/tests/binance_swap_test.rs
  39. 135 44
      exchanges/tests/bitget_spot_test.rs
  40. 74 50
      exchanges/tests/gate_swap_test.rs
  41. 133 40
      exchanges/tests/kucoin_spot_test.rs
  42. 129 107
      exchanges/tests/kucoin_swap_test.rs
  43. 243 53
      exchanges/tests/okx_swap_test.rs
  44. 67 0
      exchanges/tests/socket_tool_test.rs
  45. 20 20
      exchanges/tests/test.rs
  46. 59 0
      global/src/account_info.rs
  47. 2 5
      global/src/export_utils.rs
  48. 2 1
      global/src/lib.rs
  49. 1 1
      global/src/public_params.rs
  50. 2 0
      global/tests/export_utils_test.rs
  51. 10 0
      global/tests/get_account_info_test.rs
  52. 2 0
      standard/.gitignore
  53. 3 1
      standard/Cargo.toml
  54. 4 2
      standard/src/binance_handle.rs
  55. 50 0
      standard/src/binance_spot_handle.rs
  56. 3 1
      standard/src/bitget_spot_handle.rs
  57. 28 1
      standard/src/gate_handle.rs
  58. 12 3
      standard/src/handle_info.rs
  59. 3 1
      standard/src/kucoin_handle.rs
  60. 3 1
      standard/src/kucoin_spot_handle.rs
  61. 5 0
      standard/src/kucoin_swap.rs
  62. 6 0
      standard/src/lib.rs
  63. 5 3
      standard/src/okx_handle.rs
  64. 330 136
      standard/src/okx_swap.rs
  65. 31 0
      standard/tests/binance_handle_test.rs
  66. 1 1
      standard/tests/binance_spot_handle_test.rs
  67. 5 5
      standard/tests/bitget_spot_handle_test.rs
  68. 347 218
      standard/tests/exchange_test.rs
  69. 83 11
      standard/tests/gate_handle_test.rs
  70. 6 6
      standard/tests/kucoin_handle_test.rs
  71. 5 5
      standard/tests/kucoin_spot_handle_test.rs
  72. 7 7
      standard/tests/okx_handle_test.rs
  73. 36 2
      standard/tests/okx_swap_test.rs
  74. 5 1
      strategy/Cargo.toml
  75. 46 50
      strategy/src/binance_spot.rs
  76. 48 64
      strategy/src/binance_usdt_swap.rs
  77. 68 71
      strategy/src/bitget_spot.rs
  78. 30 15
      strategy/src/exchange_disguise.rs
  79. 55 30
      strategy/src/gate_swap.rs
  80. 41 44
      strategy/src/kucoin_spot.rs
  81. 87 59
      strategy/src/kucoin_swap.rs
  82. 6 7
      strategy/src/predictor.rs
  83. 49 28
      strategy/src/quant.rs
  84. 68 72
      strategy/src/strategy.rs
  85. 22 0
      test_account.toml.sample

+ 2 - 1
.gitignore

@@ -5,4 +5,5 @@ Cargo.lock
 config.toml*
 *.log
 *.log.*
-/logs*
+/logs*
+/test_account.toml

+ 2 - 1
Cargo.toml

@@ -29,5 +29,6 @@ members=[
     "exchanges",
     "standard",
     "strategy",
-    "global"
+    "global",
+    "derive"
 ]

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

+ 92 - 0
derive/src/binance_swap_export.rs

@@ -0,0 +1,92 @@
+use std::collections::BTreeMap;
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+use serde::{Deserialize, Serialize};
+use tracing::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 res_data = self.request.get_user_trades(symbol_format, start_time, end_time, limit_params).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 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();
+
+                data_array[index] = vec![
+                    value.id.to_string(),
+                    value.order_id.to_string(),
+                    value.symbol.clone(),
+                    value.side.clone(),
+                    value.price.clone(),
+                    value.qty.clone(),
+                    value.quote_qty.clone(),
+                    value.commission.clone(),
+                    time,
+                ];
+            }
+            global::export_utils::export_excel(header_array, data_array, prefix_name)
+        } else {
+            res_data.to_string()
+        }
+    }
+}

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

+ 46 - 0
derive/src/export_excel.rs

@@ -0,0 +1,46 @@
+use std::collections::BTreeMap;
+use crate::binance_swap_export::BinanceSwapExport;
+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,
+    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::BitgetSpot => {
+                Box::new(BitgetSpotExport::new(is_colo, params).await)
+            }
+            ExportEnum::OkxSwap => {
+                Box::new(OkxSwapExport::new(is_colo, params).await)
+            }
+        }
+    }
+}

+ 94 - 0
derive/src/gate_swap_export.rs

@@ -0,0 +1,94 @@
+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::{error, 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 res_data = self.request.my_trades(symbol_array[1].to_lowercase(), symbol_format, limit_params).await;
+        if start_time > 0 || end_time > 0 { error!("该交易所不支持根据时间查询!") };
+        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 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[index] = 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,
+                ];
+            }
+            global::export_utils::export_excel(header_array, data_array, prefix_name)
+        } else {
+            res_data.to_string()
+        }
+    }
+}

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

+ 14 - 0
derive/src/lib.rs

@@ -0,0 +1,14 @@
+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;
+
+#[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()
+        }
+    }
+}

+ 19 - 0
derive/tests/binance_swap_export_test.rs

@@ -0,0 +1,19 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "BLZ_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::BinanceSwap).await;
+    let export_trades = export.export_trades("binance_swap",SYMBOL.to_string(), 0, 0, 100).await;
+    trace!(?export_trades);
+}

+ 18 - 0
derive/tests/bitget_spot_export_test.rs

@@ -0,0 +1,18 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "LOOM_USDT";
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::BitgetSpot).await;
+    let export_trades = export.export_trades("bitget_spot",SYMBOL.to_string(), 0, 0, 100).await;
+    trace!(?export_trades);
+}

+ 77 - 0
derive/tests/export_excel_test.rs

@@ -0,0 +1,77 @@
+use std::collections::{BTreeMap};
+use tracing::trace;
+use derive::export_excel::{ExportEnum, ExportExcel};
+use derive::ExportConnector;
+use exchanges::proxy;
+
+// 创建实体
+#[allow(dead_code)]
+pub async fn test_new_export(export_enum: ExportEnum) -> Box<dyn ExportConnector> {
+    // 检测是否走代理
+    pub fn proxy_handle() {
+        if proxy::ParsingDetail::http_enable_proxy() {
+            trace!("检测有代理配置,配置走代理");
+        }
+    }
+
+    let account_info = global::account_info::get_account_info("../test_account.toml");
+
+    match export_enum {
+        ExportEnum::BinanceSwap => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.binance_access_key;
+            let secret_key = account_info.binance_secret_key;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            ExportExcel::new(ExportEnum::BinanceSwap, false, params).await
+        }
+        ExportEnum::KucoinSwap => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.kucoin_access_key;
+            let secret_key = account_info.kucoin_secret_key;
+            let pass_key = account_info.kucoin_pass;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), pass_key);
+            ExportExcel::new(ExportEnum::KucoinSwap, false, params).await
+        }
+        ExportEnum::KucoinSpot => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.kucoin_access_key;
+            let secret_key = account_info.kucoin_secret_key;
+            let pass_key = account_info.kucoin_pass;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), pass_key);
+            ExportExcel::new(ExportEnum::KucoinSpot, false, params).await
+        }
+        ExportEnum::GateSwap => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.gate_access_key;
+            let secret_key = account_info.gate_secret_key;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            ExportExcel::new(ExportEnum::GateSwap, false, params).await
+        }
+        ExportEnum::BitgetSpot => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.bitget_access_key;
+            let secret_key = account_info.bitget_secret_key;
+            let pass_key = account_info.bitget_pass;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), pass_key);
+            ExportExcel::new(ExportEnum::BitgetSpot, false, params).await
+        }
+        ExportEnum::OkxSwap => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.okx_access_key;
+            let secret_key = account_info.okx_secret_key;
+            let pass_key = account_info.okx_pass;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), pass_key);
+            ExportExcel::new(ExportEnum::OkxSwap, false, params).await
+        }
+    }
+}

+ 19 - 0
derive/tests/gate_swap_export_test.rs

@@ -0,0 +1,19 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "LOOM_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::GateSwap).await;
+    let export_trades = export.export_trades("gate_swap", SYMBOL.to_string(), 0, 0, 100).await;
+    trace!(?export_trades);
+}

+ 18 - 0
derive/tests/kucoin_spot_export_test.rs

@@ -0,0 +1,18 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "LOOM_USDT";
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::KucoinSpot).await;
+    let export_trades = export.export_trades("kucoin_spot",SYMBOL.to_string(), 0, 0, 100).await;
+    trace!(?export_trades);
+}

+ 18 - 0
derive/tests/kucoin_swap_export_test.rs

@@ -0,0 +1,18 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "LOOM_USDT";
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::KucoinSwap).await;
+    let export_trades = export.export_trades("kucoin_swap",SYMBOL.to_string(), 0, 0, 100).await;
+    trace!(?export_trades);
+}

+ 18 - 0
derive/tests/okx_swap_export_test.rs

@@ -0,0 +1,18 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "LOOM_USDT";
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::OkxSwap).await;
+    let export_trades = export.export_trades("okx_swap",SYMBOL.to_string(), 0, 0, 100).await;
+    trace!(?export_trades);
+}

+ 12 - 3
exchanges/Cargo.toml

@@ -8,7 +8,13 @@ edition = "2021"
 [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 = "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"] }
@@ -23,7 +29,7 @@ data-encoding = "2.4.0"
 
 hmac = "0.8.1"
 sha2 = "0.9.8"
-tokio-tungstenite = "0.14"
+#tokio-tungstenite = "0.14"
 
 ##代替f64避免精度丢失
 rust_decimal = "1.32.0"
@@ -33,4 +39,7 @@ rust_decimal_macros = "1.32.0"
 ##日志
 global = { path="../global" }
 tracing = "0.1"
-tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+log = "0.4.20"
+
+##

+ 131 - 266
exchanges/src/binance_spot_ws.rs

@@ -1,17 +1,14 @@
-use std::collections::{BTreeMap};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::Duration;
-use tokio::sync::mpsc::Sender;
-use tracing::{error, info, trace};
-use crate::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
-use crate::proxy::ParsingDetail;
+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, HeartbeatType};
 use crate::utils::get_time_microsecond;
 
 pub enum BinanceSpotWsType {
@@ -19,75 +16,68 @@ pub enum BinanceSpotWsType {
     PublicAndPrivate,
 }
 
-
-#[derive(Clone)]                        //订阅枚举
+//订阅频道
+#[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 {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
+    //类型
+    label: String,
+    //地址
+    address_url: String,
     //账号信息
-    login_param: BTreeMap<String, String>,
-    //kuconis特殊参数
+    login_param: Option<BinanceSpotLogin>,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
+    //订阅
     subscribe_types: Vec<BinanceSpotSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,
-    //数据通道
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl BinanceSpotWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub fn new(is_colo: bool,
-               login_param: BTreeMap<String, String>,
-               ws_type: BinanceSpotWsType,
-               sender: Sender<ResponseData>,
-    ) -> BinanceSpotWs
-    {
-        return BinanceSpotWs::new_label("default-BinanceSpotWs".to_string(), is_colo, login_param, ws_type, sender);
+    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: BTreeMap<String, String>,
-                     ws_type: BinanceSpotWsType,
-                     sender: Sender<ResponseData>,
-    ) -> BinanceSpotWs
-    {
-
-
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<BinanceSpotLogin>, ws_type: BinanceSpotWsType) -> BinanceSpotWs {
         /*******公共频道-私有频道数据组装*/
-        let request_url = match ws_type {
+        let address_url = match ws_type {
             BinanceSpotWsType::PublicAndPrivate => {
                 "wss://stream.binance.com:9443/ws".to_string()
             }
         };
 
         if is_colo {
-            info!("开启高速(未配置,走普通:{})通道",request_url);
-        }else{
-            info!("走普通通道:{}",request_url);
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
         }
         /*****返回结构体*******/
         BinanceSpotWs {
             label,
-            request_url,
-            proxy: parsing_detail,
+            address_url,
             login_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 20,
         }
     }
 
@@ -98,28 +88,33 @@ impl BinanceSpotWs {
     pub fn set_subscribe(&mut self, subscribe_types: Vec<BinanceSpotSubscribeType>) {
         self.subscribe_types.extend(subscribe_types);
     }
-    //自定义
-    pub async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        let mut symbol_s = b_array.clone();
-        for symbol in symbol_s.iter_mut() {
-            // 大写
+    //手动添加币对
+    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 = symbol_s;
-        let log_in = self.login_param.clone();
-        trace!(?log_in);
-        self.run(bool_v1).await;
+        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 {
@@ -134,221 +129,110 @@ impl BinanceSpotWs {
             }
         }
     }
-    //组装订阅数据
-    pub fn get_subscription(&self) -> Vec<String> {
-        let mut args = vec![];
+    //订阅信息生成
+    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());
-                args.push(ty_str);
+                params.push(ty_str);
             }
         }
 
-        trace!("订阅信息:{:?}", args);
-        args
+        let str = json!({
+            "method": "SUBSCRIBE",
+            "params":  params,
+            "id": 1
+            });
+        str.to_string()
     }
-
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&mut self, bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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 params = self.get_subscription();
-        let subscription_str = if params.len() > 0 {
-            serde_json::json!({
-                "method": "SUBSCRIBE",
-                "params":serde_json::Value::from(params),
-                "id": 1
-            }).to_string()
-        } else {
-            "".to_string()
-        };
-        trace!("订阅内容:{}",subscription_str);
 
-        let request_url = self.request_url.clone();
-        loop {
-            tokio::time::sleep(Duration::from_millis(5000)).await;
-            info!("要连接咯~~!!{}", request_url);
-            let request_url = Url::parse(request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                match connect_with_proxy(request_url.clone(),
-                                         proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone, ws.0, subscription_str.clone()).await;
-                    }
-                    Err(err) => {
-                        error!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone, ws.0, subscription_str.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        error!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
+        //心跳-- 方法内部线程启动
+        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 bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
+        //设置订阅
+        let mut subscribe_array = vec![];
+        if login_is {
+            //登录相关
         }
-    }
+        subscribe_array.push(subscription.to_string());
 
-    //代理
-    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>, subscription: String)
-    {
-        info!("走代理-链接成功!开始数据读取");
-        let lable = self.label.clone();
-        /*****订阅消息**/
-        if subscription.len() > 0 {
-            web_socket.write_message(Message::Text(subscription)).unwrap();
-        }
-        /*****消息溜***/
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    // trace!("获取推送:{}",text.clone());
-                    let mut res_data = Self::ok_text(lable.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    // let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!("Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!("Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
+        //链接
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array,
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("币安-现货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-        web_socket.close(None).unwrap();
+        Ok(())
     }
-
-    //非代理
-    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>, subscription: String)
-    {
-        info!("链接成功!开始数据读取");
-        let lable = self.label.clone();
-        /*****订阅消息**/
-        if subscription.len() > 0 {
-            web_socket.write_message(Message::Text(subscription.clone())).unwrap();
-        }
-        /*****消息溜***/
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    // trace!("获取推送:{}",text.clone());
-                    let mut res_data = Self::ok_text(lable.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", subscription.clone());
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    // let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
-
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-201" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
+            _ => None
         }
-        web_socket.close(None).unwrap();
     }
-
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
+    }
     //数据解析
-    pub fn ok_text(lable: String, text: String) -> ResponseData
-    {
+    pub fn ok_text(text: String) -> ResponseData {
         // trace!("原始数据");
         // trace!(?text);
-        let mut res_data = ResponseData::new(lable, "200".to_string(), "success".to_string(), "".to_string());
+        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 = "-200".to_string();
+            res_data.code = "-201".to_string();
             res_data.channel = "".to_string();
             res_data.message = "订阅成功!".to_string();
         } else if json_value.get("e").is_some() &&
@@ -373,25 +257,6 @@ impl BinanceSpotWs {
         } else {
             res_data.channel = "未知的频道".to_string();
         }
-        //
-        // if json_value.get("error").is_some() {//订阅返回
-        //     res_data.code = json_value["error"]["code"].to_string();
-        //     res_data.message = json_value["error"]["msg"].to_string();
-        // } else if json_value.get("stream").is_some() {//订阅返回
-        //     res_data.data = format!("{}", json_value.get("data").as_ref().unwrap());
-        //     res_data.code = "200".to_string();
-        //
-        //     let channel = format!("{}", json_value.get("stream").as_ref().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 {}
-        // } else {
-        //     res_data.data = text
-        // }
         res_data
     }
 }

+ 24 - 0
exchanges/src/binance_swap_rest.rs

@@ -246,6 +246,30 @@ impl BinanceSwapRest {
         ).await;
         data
     }
+    //账户成交历史
+    pub async fn get_user_trades(&mut self, symbol: String, start_time: i64, end_time: i64, limit: i64) -> ResponseData {
+        let mut params = serde_json::json!({
+            "symbol":symbol,
+            "limit":1000,
+            "recvWindow":"1000",
+         });
+        if start_time > 0 {
+            params["startTime"] = json!(start_time);
+        }
+        if end_time > 0 {
+            params["endTime"] = json!(end_time);
+        }
+        if limit > 0 {
+            params["limit"] = json!(limit);
+        }
+        let data = self.request("GET".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/userTrades"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
 
 
     /*******************************************************************************************************/

+ 145 - 245
exchanges/src/binance_swap_ws.rs

@@ -1,91 +1,83 @@
-use std::collections::{BTreeMap};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::Duration;
-use tokio::sync::mpsc::Sender;
-use tracing::{error, info, trace};
-use crate::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
-use crate::proxy::ParsingDetail;
+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, HeartbeatType};
 use crate::utils::get_time_microsecond;
 
-pub enum BinanceWsType {
-    //订阅频道类型
+//类型
+pub enum BinanceSwapWsType {
     PublicAndPrivate,
 }
 
-
-#[derive(Clone)]                        //订阅枚举
-pub enum BinanceSubscribeType {
+//订阅频道
+#[derive(Clone)]
+pub enum BinanceSwapSubscribeType {
     PuBookTicker,
     PuAggTrade,
     PuDepth20levels100ms,
 }
 
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct BinanceSwapLogin {
+    pub api_key: String,
+    pub api_secret: String,
+}
+
 #[derive(Clone)]
+#[allow(dead_code)]
 pub struct BinanceSwapWs {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
-    //账号信息
-    login_param: BTreeMap<String, String>,
-    //kuconis特殊参数
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<BinanceSwapLogin>,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
-    subscribe_types: Vec<BinanceSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,
-    //数据通道
+    //订阅
+    subscribe_types: Vec<BinanceSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl BinanceSwapWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub fn new(is_colo: bool,
-               login_param: BTreeMap<String, String>,
-               ws_type: BinanceWsType,
-               sender: Sender<ResponseData>,
-    ) -> BinanceSwapWs
-    {
-        return BinanceSwapWs::new_label("default-BinanceSwapWs".to_string(), is_colo, login_param, ws_type, sender);
+    pub fn new(is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
+        return BinanceSwapWs::new_label("default-BinanceSwapWs".to_string(), is_colo, login_param, ws_type);
     }
-    pub fn new_label(label: String, is_colo: bool,
-                     login_param: BTreeMap<String, String>,
-                     ws_type: BinanceWsType,
-                     sender: Sender<ResponseData>,
-    ) -> BinanceSwapWs
-    {
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<BinanceSwapLogin>, ws_type: BinanceSwapWsType) -> BinanceSwapWs {
         /*******公共频道-私有频道数据组装*/
-        let request_url = match ws_type {
-            BinanceWsType::PublicAndPrivate => {
+        let address_url = match ws_type {
+            BinanceSwapWsType::PublicAndPrivate => {
+                // "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms".to_string(),
                 "wss://fstream.binance.com/stream".to_string()
             }
         };
 
         if is_colo {
-            info!("开启高速(未配置,走普通:{})通道",request_url);
+            info!("开启高速(未配置,走普通:{})通道",address_url);
         } else {
-            info!("走普通通道:{}",request_url);
+            info!("走普通通道:{}",address_url);
         }
-        /*****返回结构体*******/
+
         BinanceSwapWs {
             label,
-            request_url,
-            proxy: parsing_detail,
+            address_url,
             login_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 20,
         }
     }
 
@@ -93,247 +85,155 @@ impl BinanceSwapWs {
     /*****************************************订阅函数********************************************************/
     /*******************************************************************************************************/
     //手动添加订阅信息
-    pub fn set_subscribe(&mut self, subscribe_types: Vec<BinanceSubscribeType>) {
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BinanceSwapSubscribeType>) {
         self.subscribe_types.extend(subscribe_types);
     }
-    //自定义
-    pub async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        let mut symbol_s = b_array.clone();
-        for symbol in symbol_s.iter_mut() {
-            // 大写
+    //手动添加币对
+    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 = symbol_s;
-        let log_in = self.login_param.clone();
-        trace!(?log_in);
-        self.run(bool_v1).await;
+        self.symbol_s = b_array;
+    }
+    //频道是否需要登录
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                BinanceSwapSubscribeType::PuBookTicker => false,
+                BinanceSwapSubscribeType::PuAggTrade => false,
+                BinanceSwapSubscribeType::PuDepth20levels100ms => false,
+            } {
+                return true;
+            }
+        }
+        false
     }
-
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/
-
-
     //订阅枚举解析
-    pub fn enum_to_string(symbol: String, subscribe_type: BinanceSubscribeType) -> String {
+    pub fn enum_to_string(symbol: String, subscribe_type: BinanceSwapSubscribeType) -> String {
         match subscribe_type {
-            BinanceSubscribeType::PuAggTrade => {
+            BinanceSwapSubscribeType::PuAggTrade => {
                 format!("{}@aggTrade", symbol)
             }
-            BinanceSubscribeType::PuDepth20levels100ms => {
+            BinanceSwapSubscribeType::PuDepth20levels100ms => {
                 format!("{}@depth20@100ms", symbol)
             }
-            BinanceSubscribeType::PuBookTicker => {
+            BinanceSwapSubscribeType::PuBookTicker => {
                 format!("{}@bookTicker", symbol)
             }
         }
     }
-    //组装订阅数据
+    //订阅信息生成
     pub fn get_subscription(&self) -> String {
-        let mut str = "".to_string();
-        // let mut args = vec![];
+        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());
-                // args.push(ty_str);
-                if str.len() > 0 {
-                    str = format!("{}/", str)
-                }
-                str = format!("{}{}", str, ty_str.clone().to_string());
+                params.push(ty_str);
             }
         }
 
-        trace!("订阅信息:{}", str.to_string());
+        let str = json!({
+            "method": "SUBSCRIBE",
+            "params":  params,
+            "id": 1
+            });
         str.to_string()
     }
-
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&mut self, bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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 request_url = if subscription.len() > 0 {
-            format!("{}?streams={}", self.request_url, subscription)
-            // format!("{}", self.request_url)
-        } else {
-            format!("{}", self.request_url)
-        };
+        let address_url = self.address_url.clone();
+        let label = self.label.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
 
-        loop {
-            tokio::time::sleep(Duration::from_millis(5000)).await;
-            info!("要连接咯~~!!{}", request_url);
-            let request_url = Url::parse(request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                match connect_with_proxy(request_url.clone(),
-                                         proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        error!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        error!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
-
-            let bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-    }
 
-    //代理
-    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>, _subscription: String)
-    {
-        info!("走代理-链接成功!开始数据读取");
-        let lable = self.label.clone();
-        /*****消息溜***/
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    // trace!("获取推送:{}",text.clone());
-                    let mut res_data = Self::ok_text(lable.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    // let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!("Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!("Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
+        //心跳-- 方法内部线程启动
+        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 bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
+        //设置订阅
+        let mut subscribe_array = vec![];
+        if login_is {
+            //登录相关
         }
-        web_socket.close(None).unwrap();
-    }
+        subscribe_array.push(subscription.to_string());
 
-    //非代理
-    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>, _subscription: String)
-    {
-        info!("链接成功!开始数据读取");
-        let lable = self.label.clone();
-        /*****消息溜***/
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    // trace!("获取推送:{}",text.clone());
-                    let mut res_data = Self::ok_text(lable.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone())) ;
-                    // let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone())) ;
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone())) ;
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应") ;
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error) ;
-                    break;
-                }
-                _ => {}
-            }
+        //链接
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array,
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("币安-期货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-201" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
+            _ => None
         }
-        web_socket.close(None).unwrap();
     }
-
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
+    }
     //数据解析
-    pub fn ok_text(lable: String, text: String) -> ResponseData
-    {
+    pub fn ok_text(text: String) -> ResponseData {
         // trace!("原始数据");
         // trace!(?text);
-        let mut res_data = ResponseData::new(lable, "200".to_string(), "success".to_string(), "".to_string());
+        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("error").is_some() {//订阅返回
+        if json_value.get("result").is_some() && json_value.get("id").is_some() &&
+            json_value.get("id").unwrap() == 1
+        {
+            res_data.code = "-201".to_string();
+            res_data.message = "订阅成功".to_string();
+        } else if json_value.get("error").is_some() {//订阅返回
             res_data.code = json_value["error"]["code"].to_string();
             res_data.message = json_value["error"]["msg"].to_string();
         } else if json_value.get("stream").is_some() {//订阅返回

+ 43 - 9
exchanges/src/bitget_spot_rest.rs

@@ -32,9 +32,9 @@ impl BitgetSpotRest {
 
     pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BitgetSpotRest
     {
-        return BitgetSpotRest::new_lable("default-BitgetSpotRest".to_string(), is_colo, login_param);
+        return BitgetSpotRest::new_label("default-BitgetSpotRest".to_string(), is_colo, login_param);
     }
-    pub fn new_lable(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BitgetSpotRest {
+    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 {
@@ -224,12 +224,19 @@ impl BitgetSpotRest {
     }
     //获取历史成交数据
     pub async fn get_market_fills_history(&mut self, symbol: String, start_time: String, end_time: String, limit: String) -> ResponseData {
-        let params = serde_json::json!({
+        let mut params = serde_json::json!({
             "symbol":symbol,
-            "startTime":start_time,
-            "endTime":end_time,
-            "limit":limit,
+            "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(),
@@ -561,6 +568,33 @@ impl BitgetSpotRest {
         ).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
+    }
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/
@@ -744,11 +778,11 @@ impl BitgetSpotRest {
 
     //res_data 解析
     pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
-        trace!("原始数据:{:?}",result);
+        // trace!("原始数据:{:?}",result);
         match result {
             Ok(res_data) => {
                 if res_data.code != "200" {
-                    trace!("不等于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();
@@ -759,7 +793,7 @@ impl BitgetSpotRest {
                                                   format!("请求地址:{},请求参数:{}", base_url, params));
                     error
                 } else {
-                    trace!("等于200");
+                    // trace!("等于200");
                     let body: String = res_data.data;
                     let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
 

+ 322 - 303
exchanges/src/bitget_spot_ws.rs

@@ -1,33 +1,28 @@
-use std::collections::{BTreeMap};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::thread;
+use std::sync::atomic::AtomicBool;
 use std::time::Duration;
+
 use chrono::Utc;
-use tokio::sync::mpsc::Sender;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use ring::hmac;
 use serde_json::{json, Value};
-use tracing::{error, info, trace};
-use crate::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
-use crate::proxy::ParsingDetail;
+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};
 use crate::utils::get_time_microsecond;
 
-use ring::hmac;
-
-pub enum BitgetWsType {
-    //频道类型
+//类型
+pub enum BitgetSpotWsType {
     Public,
     Private,
 }
 
-
-#[derive(Clone)]                        //订阅枚举
-pub enum BitgetSubscribeType {
+//订阅频道
+#[derive(Clone)]
+pub enum BitgetSpotSubscribeType {
     PuTicker,
     PuCandle1m,
     PuTrade,
@@ -37,67 +32,64 @@ pub enum BitgetSubscribeType {
     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 {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
-    //账号信息
-    login_param: BTreeMap<String, String>,
-    //kuconis特殊参数
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<BitgetSpotLogin>,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
-    subscribe_types: Vec<BitgetSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,     //数据通道
+    //订阅
+    subscribe_types: Vec<BitgetSpotSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl BitgetSpotWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub fn new(is_colo: bool,
-               login_param: BTreeMap<String, String>,
-               ws_type: BitgetWsType,
-               sender: Sender<ResponseData>,
-    ) -> BitgetSpotWs
-    {
-        return BitgetSpotWs::new_label("default-BitgetSpotWs".to_string(), is_colo, login_param, ws_type, sender);
+    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: BTreeMap<String, String>,
-                     ws_type: BitgetWsType,
-                     sender: Sender<ResponseData>,
-    ) -> BitgetSpotWs
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<BitgetSpotLogin>, ws_type: BitgetSpotWsType) -> BitgetSpotWs
     {
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
-
-        let request_url = match ws_type {
-            BitgetWsType::Public => {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            BitgetSpotWsType::Public => {
                 format!("wss://ws.bitget.com/v2/ws/public")
             }
-            BitgetWsType::Private => {
+            BitgetSpotWsType::Private => {
                 format!("wss://ws.bitget.com/v2/ws/private")
             }
         };
 
         if is_colo {
-            info!("开启高速(未配置,走普通:{})通道",request_url);
+            info!("开启高速(未配置,走普通:{})通道",address_url);
         } else {
-            info!("走普通通道:{}",request_url);
+            info!("走普通通道:{}",address_url);
         }
-        /*****返回结构体*******/
+
         BitgetSpotWs {
             label,
-            request_url,
-            proxy: parsing_detail,
+            address_url,
             login_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 20,
         }
     }
 
@@ -105,66 +97,79 @@ impl BitgetSpotWs {
     /*****************************************订阅函数********************************************************/
     /*******************************************************************************************************/
     //手动添加订阅信息
-    pub fn set_subscribe(&mut self, subscribe_types: Vec<BitgetSubscribeType>) {
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BitgetSpotSubscribeType>) {
         self.subscribe_types.extend(subscribe_types);
     }
-    //自定义
-    pub async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        let mut symbol_s = b_array.clone();
-        for symbol in symbol_s.iter_mut() {
-            // 大写
+    //手动添加币对
+    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 = symbol_s;
-        self.run(bool_v1).await;
+        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: BitgetSubscribeType) -> Value {
+    pub fn enum_to_string(symbol: String, subscribe_type: BitgetSpotSubscribeType) -> Value {
         match subscribe_type {
-            BitgetSubscribeType::PuTicker => {
+            BitgetSpotSubscribeType::PuTicker => {
                 json!({
                     "instType": "SPOT",
                     "channel": "ticker",
                     "instId": symbol,
                 })
             }
-            BitgetSubscribeType::PuCandle1m => {
+            BitgetSpotSubscribeType::PuCandle1m => {
                 json!({
                     "instType": "SPOT",
                     "channel": "candle1m",
                     "instId": symbol,
                 })
             }
-            BitgetSubscribeType::PuTrade => {
+            BitgetSpotSubscribeType::PuTrade => {
                 json!({
                     "instType": "SPOT",
                     "channel": "trade",
                     "instId": symbol,
                 })
             }
-            BitgetSubscribeType::PuBooks5 => {
+            BitgetSpotSubscribeType::PuBooks5 => {
                 json!({
                     "instType": "SPOT",
                     "channel": "books5",
                     "instId": symbol,
                 })
             }
-            BitgetSubscribeType::PrAccount => {
+            BitgetSpotSubscribeType::PrAccount => {
                 json!({
                     "instType": "SPOT",
                     "channel": "account",
                     "coin": "default"
                 })
             }
-            BitgetSubscribeType::PrOrders => {
+            BitgetSpotSubscribeType::PrOrders => {
                 json!({
                     "instType": "SPOT",
                     "channel": "orders",
@@ -173,46 +178,35 @@ impl BitgetSpotWs {
             }
         }
     }
-    //组装订阅数据
+    //订阅信息生成
     pub fn get_subscription(&self) -> String {
-        let mut args = vec![];
+        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());
-                args.push(ty_str);
+                params.push(ty_str);
             }
         }
         let str = json!({
             "op": "subscribe",
-            "args": args
+            "args": params
         });
 
         str.to_string()
     }
-
-    // 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
-    // }
-
-    //组装登陆数据
+    //登录数据组装
     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();
-        for (key, value) in &self.login_param {
-            if key == "access_key" {
-                access_key = value.parse().unwrap();
-            } else if key == "secret_key" {
-                secret_key = value.parse().unwrap();
-            } else if key == "pass_key" {
-                passphrase = value.parse().unwrap();
+        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 {
@@ -242,232 +236,257 @@ impl BitgetSpotWs {
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&mut self, bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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();
-        loop {
-            tokio::time::sleep(Duration::from_millis(5000)).await;
-            trace!("要连接咯~~!!{}", self.request_url);
+        let address_url = self.address_url.clone();
+        let label = self.label.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
 
-            let request_url = Url::parse(self.request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                match connect_with_proxy(request_url.clone(),
-                                         proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        error!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        error!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
 
-            let bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-    }
+        //心跳-- 方法内部线程启动
+        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!("线程-异步心跳-结束");
+        });
 
-    //代理
-    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>, subscription: String)
-    {
-        info!("走代理-链接成功!开始数据读取");
-        let label = self.label.clone();
-        /*****登陆***/
-        let login_str = self.log_in_to_str();
-        if login_str != "" {
-            let _ = web_socket.write_message(Message::Text(login_str));
-            tokio::time::sleep(Duration::from_secs(3)).await;
-        }
-        /*****订阅***/
-        if subscription.len() > 0 {
-            info!("订阅信息:{}", subscription);
-            web_socket.write_message(Message::Text(subscription.clone()))
-                .unwrap();
+        //设置订阅
+        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;
         }
-        /*****消息溜***/
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    if (get_time - ping_timeout) >= (1000 * 30) {
-                        trace!("30s 一次主动发送心跳包!");
-                        let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
-                        ping_timeout = get_time;
-                    }
+        subscribe_array.push(subscription.to_string());
 
-                    // let st_time = get_time_microsecond();
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    // trace!("解析数据耗时::{}", st_time  - get_time_microsecond());
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else if res_data.code == "-201" {
-                        trace!("登陆:{:?}", res_data);
-                    } else if res_data.code == "-202" {
-                        trace!("空数据不予返回:{:?}", res_data);
-                    } else {
-                        let sender = self.sender.clone();
-                        // let prev_time = Utc::now().timestamp_micros();
-                        tokio::spawn(async move {
-                            // info!("{:04} {}", Utc::now().timestamp_micros() - prev_time, res_data.channel);
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                    break;
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
+        //链接
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array,
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("币安-期货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-        web_socket.close(None).unwrap();
+        Ok(())
     }
 
-    //非代理
-    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
-                          subscription: String)
-    {
-        info!("链接成功!开始数据读取");
-        let label = self.label.clone();
-        /*****登陆***/
-        let login_str = self.log_in_to_str();
-        if login_str != "" {
-            let _ = web_socket.write_message(Message::Text(login_str));
-            thread::sleep(Duration::from_secs(3));
-        }
-        /*****订阅***/
-        if subscription.len() > 0 {
-            trace!("订阅信息:{}",subscription);
-            web_socket.write_message(Message::Text(subscription))
-                .unwrap();
-        }
-        /*****消息溜***/
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    if (get_time - ping_timeout) >= (1000 * 30) {
-                        trace!("30s 一次主动发送心跳包!");
-                        let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
-                        ping_timeout = get_time;
-                    }
-
-                    // let st_time = get_time_microsecond();
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    // trace!("解析数据耗时::{}", st_time  - get_time_microsecond());
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else if res_data.code == "-201" {
-                        trace!("登陆:{:?}", res_data);
-                    } else if res_data.code == "-202" {
-                        trace!("空数据不予返回:{:?}", res_data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                    break;
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
+    // //代理
+    // async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>, subscription: String)
+    // {
+    //     info!("走代理-链接成功!开始数据读取");
+    //     let label = self.label.clone();
+    //     /*****登陆***/
+    //     let login_str = self.log_in_to_str();
+    //     if login_str != "" {
+    //         let _ = web_socket.write_message(Message::Text(login_str));
+    //         tokio::time::sleep(Duration::from_secs(3)).await;
+    //     }
+    //     /*****订阅***/
+    //     if subscription.len() > 0 {
+    //         info!("订阅信息:{}", subscription);
+    //         web_socket.write_message(Message::Text(subscription.clone()))
+    //             .unwrap();
+    //     }
+    //     /*****消息溜***/
+    //     let mut ping_timeout = chrono::Utc::now().timestamp_millis();
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(1)).await;
+    //         let msg = web_socket.read_message();
+    //         match msg {
+    //             Ok(Message::Text(text)) => {
+    //                 let get_time = chrono::Utc::now().timestamp_millis();
+    //                 if (get_time - ping_timeout) >= (1000 * 30) {
+    //                     trace!("30s 一次主动发送心跳包!");
+    //                     let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
+    //                     ping_timeout = get_time;
+    //                 }
+    //
+    //                 // let st_time = get_time_microsecond();
+    //                 let mut res_data = Self::ok_text(label.to_string(), text);
+    //                 // trace!("解析数据耗时::{}", st_time  - get_time_microsecond());
+    //                 res_data.time = get_time_microsecond();
+    //                 if res_data.code == "-200" {
+    //                     trace!("登陆:{:?}", res_data);
+    //                 } else if res_data.code == "-201" {
+    //                     trace!("订阅成功:{:?}", res_data.data);
+    //                 } else if res_data.code == "-202" {
+    //                     trace!("空数据不予返回:{:?}", res_data);
+    //                 } else {
+    //                     let sender = self.sender.clone();
+    //                     // let prev_time = Utc::now().timestamp_millis();
+    //                     tokio::spawn(async move {
+    //                         // info!("{:04} {}", Utc::now().timestamp_millis() - prev_time, res_data.channel);
+    //                         sender.send(res_data).await.unwrap();
+    //                     });
+    //                     tokio::spawn(async move {});
+    //                 }
+    //             }
+    //             Ok(Message::Ping(s)) => {
+    //                 trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
+    //                 let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
+    //                 trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Pong(s)) => {
+    //                 // trace!("Pong-响应--{:?}", String::from_utf8(s));
+    //                 trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Close(_)) => {
+    //                 // trace!("socket 关闭: ");
+    //                 trace!( "Close-响应");
+    //                 break;
+    //             }
+    //             Err(error) => {
+    //                 // trace!("Error receiving message: {}", error);
+    //                 error!( "Err-响应{}", error);
+    //                 break;
+    //             }
+    //             _ => {}
+    //         }
+    //
+    //         let bool_v1_v = bool_v1.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    //     web_socket.close(None).unwrap();
+    // }
+    //
+    // //非代理
+    // async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
+    //                       subscription: String)
+    // {
+    //     info!("链接成功!开始数据读取");
+    //     let label = self.label.clone();
+    //     /*****登陆***/
+    //     let login_str = self.log_in_to_str();
+    //     if login_str != "" {
+    //         let _ = web_socket.write_message(Message::Text(login_str));
+    //         thread::sleep(Duration::from_secs(3));
+    //     }
+    //     /*****订阅***/
+    //     if subscription.len() > 0 {
+    //         trace!("订阅信息:{}",subscription);
+    //         web_socket.write_message(Message::Text(subscription))
+    //             .unwrap();
+    //     }
+    //     /*****消息溜***/
+    //     let mut ping_timeout = chrono::Utc::now().timestamp_millis();
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(1)).await;
+    //         let msg = web_socket.read_message();
+    //         match msg {
+    //             Ok(Message::Text(text)) => {
+    //                 let get_time = chrono::Utc::now().timestamp_millis();
+    //                 if (get_time - ping_timeout) >= (1000 * 30) {
+    //                     trace!("30s 一次主动发送心跳包!");
+    //                     let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
+    //                     ping_timeout = get_time;
+    //                 }
+    //
+    //                 // let st_time = get_time_microsecond();
+    //                 let mut res_data = Self::ok_text(label.to_string(), text);
+    //                 // trace!("解析数据耗时::{}", st_time  - get_time_microsecond());
+    //                 res_data.time = get_time_microsecond();
+    //                 if res_data.code == "-200" {
+    //                     trace!("订阅成功:{:?}", res_data.data);
+    //                 } else if res_data.code == "-201" {
+    //                     trace!("登陆:{:?}", res_data);
+    //                 } else if res_data.code == "-202" {
+    //                     trace!("空数据不予返回:{:?}", res_data);
+    //                 } else {
+    //                     let sender = self.sender.clone();
+    //                     tokio::spawn(async move {
+    //                         sender.send(res_data).await.unwrap();
+    //                     });
+    //                     tokio::spawn(async move {});
+    //                 }
+    //             }
+    //             Ok(Message::Ping(s)) => {
+    //                 trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
+    //                 let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
+    //                 trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Pong(s)) => {
+    //                 // trace!("Pong-响应--{:?}", String::from_utf8(s));
+    //                 trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Close(_)) => {
+    //                 // trace!("socket 关闭: ");
+    //                 trace!( "Close-响应");
+    //                 break;
+    //             }
+    //             Err(error) => {
+    //                 // trace!("Error receiving message: {}", error);
+    //                 error!( "Err-响应{}", error);
+    //                 break;
+    //             }
+    //             _ => {}
+    //         }
+    //
+    //         let bool_v1_v = bool_v1.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    //     web_socket.close(None).unwrap();
+    // }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-201" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
-
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+            "-200" => {
+                trace!("登录成功:{:?}", response_data);
+                None
+            }
+            "-202" => {
+                trace!("未知解析:{:?}", response_data);
+                None
             }
+            _ => None
         }
-        web_socket.close(None).unwrap();
     }
-
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
+    }
     //数据解析
-    pub fn ok_text(label: String, text: String) -> ResponseData
+    pub fn ok_text(text: String) -> ResponseData
     {
         // trace!("原始数据:{}", text);
         // trace!("大小:{}", text.capacity());
-        let mut res_data = ResponseData::new(label.to_string(), "200".to_string(), "success".to_string(), "".to_string());
+        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}
@@ -478,11 +497,11 @@ impl BitgetSpotWs {
                 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 = "-201".to_string();
+            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 = "-200".to_string();
+            res_data.code = "-201".to_string();
             res_data.data = text;
             res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
             res_data

+ 8 - 3
exchanges/src/gate_swap_rest.rs

@@ -352,9 +352,14 @@ impl GateSwapRest {
         data
     }
     //查询个人成交记录
-    pub async fn my_trades(&mut self, settle: String) -> ResponseData {
-        let params = serde_json::json!({
-             });
+    pub async fn my_trades(&mut self, settle: String, contract: String, limit: i64) -> ResponseData {
+        let mut params = serde_json::json!({
+            "contract":contract,
+            "limit":1000
+        });
+        if limit > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
 
         let data = self.request("GET".to_string(),
                                 "/api/v4".to_string(),

+ 296 - 279
exchanges/src/gate_swap_ws.rs

@@ -1,100 +1,90 @@
-use std::collections::{BTreeMap};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::Duration;
-use tokio::sync::mpsc::Sender;
-use serde_json::{json, Value};
+use std::sync::atomic::AtomicBool;
+
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use hex;
 use hmac::{Hmac, Mac, NewMac};
+use serde_json::{json, Value};
 use sha2::Sha512;
-use tracing::{error, info, trace};
-use crate::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
-use crate::proxy::ParsingDetail;
+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};
 use crate::utils::get_time_microsecond;
 
-pub enum GateWsType {
-    //频道类型
+//类型
+pub enum GateSwapWsType {
     PublicAndPrivate(String),
 }
 
 
-#[derive(Clone)]                        //订阅枚举
-pub enum GateSubscribeType {
+//订阅频道
+#[derive(Clone)]
+pub enum GateSwapSubscribeType {
     PuFuturesOrderBook,
     PuFuturesCandlesticks,
     PuFuturesTrades,
-    //
+
     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 {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
+    //类型
+    label: String,
+    //地址
+    address_url: String,
     //账号信息
-    login_param: BTreeMap<String, String>,
-    //kuconis特殊参数
+    login_param: Option<GateSwapLogin>,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
-    subscribe_types: Vec<GateSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,     //数据通道
+    //订阅
+    subscribe_types: Vec<GateSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl GateSwapWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub fn new(is_colo: bool,
-               login_param: BTreeMap<String, String>,
-               ws_type: GateWsType,
-               sender: Sender<ResponseData>,
-    ) -> GateSwapWs
-    {
-        return GateSwapWs::new_label("default-GateSwapWs".to_string(), is_colo, login_param, ws_type, sender);
+    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: BTreeMap<String, String>,
-                     ws_type: GateWsType,
-                     sender: Sender<ResponseData>,
-    ) -> GateSwapWs
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<GateSwapLogin>, ws_type: GateSwapWsType) -> GateSwapWs
     {
-
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
-
-        let request_url = match ws_type {
-            GateWsType::PublicAndPrivate(name) => {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            GateSwapWsType::PublicAndPrivate(name) => {
                 format!("wss://fx-ws.gateio.ws/v4/ws/{}", name.to_string())
             }
         };
 
         if is_colo {
-            info!("开启高速(未配置,走普通:{})通道",request_url);
+            info!("开启高速(未配置,走普通:{})通道",address_url);
         } else {
-            info!("走普通通道:{}",request_url);
+            info!("走普通通道:{}",address_url);
         }
-        /*****返回结构体*******/
         GateSwapWs {
             label,
-            request_url,
-            proxy: parsing_detail,
+            address_url,
             login_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 20,
         }
     }
 
@@ -102,40 +92,55 @@ impl GateSwapWs {
     /*****************************************订阅函数********************************************************/
     /*******************************************************************************************************/
     //手动添加订阅信息
-    pub fn set_subscribe(&mut self, subscribe_types: Vec<GateSubscribeType>) {
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<GateSwapSubscribeType>) {
         self.subscribe_types.extend(subscribe_types);
     }
-    //自定义
-    pub async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        let mut symbol_s = b_array.clone();
-        for symbol in symbol_s.iter_mut() {
+    //手动添加币对
+    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 = symbol_s;
-        self.run(bool_v1).await;
+        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::PrFuturesOrders(_) => true,
+                GateSwapSubscribeType::PrFuturesPositions(_) => true,
+                GateSwapSubscribeType::PrFuturesBalances(_) => true,
+            } {
+                return true;
+            }
+        }
+        false
     }
 
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/
     //订阅枚举解析
-    pub fn enum_to_string(symbol: String, subscribe_type: GateSubscribeType, login_param: BTreeMap<String, String>) -> Value {
+    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();
-        if login_param.contains_key("access_key") {
-            access_key = login_param.get("access_key").unwrap().to_string();
-        }
+        let mut  access_key = "".to_string();
         let mut secret_key = "".to_string();
-        if login_param.contains_key("secret_key") {
-            secret_key = login_param.get("secret_key").unwrap().to_string();
+        match login_param {
+            None => {}
+            Some(param) => {
+                 access_key = param.api_key.clone();
+                 secret_key = param.secret.clone();
+            }
         }
 
         match subscribe_type {
-            GateSubscribeType::PuFuturesOrderBook => {
+            GateSwapSubscribeType::PuFuturesOrderBook => {
                 json!({
                     "time": time,
                     "channel": "futures.order_book",
@@ -143,7 +148,7 @@ impl GateSwapWs {
                     "payload":  [symbol, "20", "0"]
                 })
             }
-            GateSubscribeType::PuFuturesCandlesticks => {
+            GateSwapSubscribeType::PuFuturesCandlesticks => {
                 json!({
                     "time": time,
                     "channel": "futures.candlesticks",
@@ -151,7 +156,7 @@ impl GateSwapWs {
                     "payload":  ["1m", symbol]
                 })
             }
-            GateSubscribeType::PrFuturesOrders(user_id) => {
+            GateSwapSubscribeType::PrFuturesOrders(user_id) => {
                 json!({
                     "time": time,
                     "channel": "futures.orders",
@@ -167,7 +172,7 @@ impl GateSwapWs {
                     }
                 })
             }
-            GateSubscribeType::PrFuturesPositions(user_id) => {
+            GateSwapSubscribeType::PrFuturesPositions(user_id) => {
                 json!({
                     "time": time,
                     "channel": "futures.positions",
@@ -183,7 +188,7 @@ impl GateSwapWs {
                     }
                 })
             }
-            GateSubscribeType::PrFuturesBalances(user_id) => {
+            GateSwapSubscribeType::PrFuturesBalances(user_id) => {
                 json!({
                     "time": time,
                     "channel": "futures.balances",
@@ -199,7 +204,7 @@ impl GateSwapWs {
                     }
                 })
             }
-            GateSubscribeType::PuFuturesTrades => {
+            GateSwapSubscribeType::PuFuturesTrades => {
                 json!({
                     "time": time,
                     "channel": "futures.trades",
@@ -209,7 +214,7 @@ impl GateSwapWs {
             }
         }
     }
-    //组装订阅数据
+    //订阅信息生成
     pub fn get_subscription(&self) -> Vec<Value> {
         let mut args = vec![];
         for symbol in &self.symbol_s {
@@ -223,7 +228,7 @@ impl GateSwapWs {
         }
         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");
@@ -235,233 +240,245 @@ impl GateSwapWs {
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&mut self ,bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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();
-        loop {
-            tokio::time::sleep(Duration::from_millis(5000)).await;
-            trace!("要连接咯~~!!{}", self.request_url);
-
-            let request_url = Url::parse(self.request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                match connect_with_proxy(request_url.clone(),
-                                         proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone ,ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        error!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone,ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        error!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
-
-            let bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-    }
-
-    //代理
-    async fn proxy_subscription(&self,bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>, subscription: Vec<Value>)
-    {
-        info!("走代理-链接成功!开始数据读取");
+        let address_url = self.address_url.clone();
         let label = self.label.clone();
-        /*****订阅***/
-        for sub in &subscription {
-            trace!("订阅信息:{}", sub.to_string());
-            web_socket.write_message(Message::Text(sub.to_string()))
-                .unwrap();
-        }
-        /*****消息溜***/
-        let mut start_ping = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    // trace!("获取推送:{}",text.clone());
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        // self.sender.send(res_data).await.unwrap();
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
+        let heartbeat_time = self.heartbeat_time.clone();
 
-                        //主动ping 服务器
-                        let new_time = chrono::Utc::now().timestamp_millis();
-                        // let tr = format!("判断-ping {}--{},{},{}",new_time,start_ping,(new_time - start_ping), (new_time - start_ping) > 10000);
-                        // trace!(tr);
-                        if (new_time - start_ping) > 10000 {
-                            let t = chrono::Utc::now().timestamp();
-                            let ping_str = serde_json::json!({
-                                "time" :t, "channel" : "futures.ping"
-                            });
-                            let _ = web_socket.write_message(Message::Ping(Vec::from(ping_str.to_string())));
-                            start_ping = new_time;
-                        }
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                    break;
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-        web_socket.close(None).unwrap();
-    }
+        //心跳-- 方法内部线程启动
+        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!("线程-异步心跳-结束");
+        });
 
-    //非代理
-    async fn subscription(&self,bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
-                          subscription: Vec<Value>)
-    {
-        info!("链接成功!开始数据读取");
-        let label = self.label.clone();
-        /*****订阅***/
-        for sub in &subscription {
-            trace!("订阅信息:{}", sub.to_string());
-            web_socket.write_message(Message::Text(sub.to_string()))
-                .unwrap();
+        //设置订阅
+        let mut subscribe_array = vec![];
+        if login_is {
+            //登录相关
+        }
+        for s in subscription {
+            subscribe_array.push(s.to_string());
         }
-        /*****消息溜***/
-        let mut start_ping = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    // trace!("获取推送:{}",text.clone());
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        // self.sender.send(res_data).await.unwrap();
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
 
-                        //主动ping 服务器
-                        let new_time = chrono::Utc::now().timestamp_millis();
-                        // let tr = format!("判断-ping {}--{},{},{}",new_time,start_ping,(new_time - start_ping), (new_time - start_ping) > 10000);
-                        // trace!(tr);
-                        if (new_time - start_ping) > 10000 {
-                            let t = chrono::Utc::now().timestamp();
-                            let ping_str = serde_json::json!({
-                                "time" :t, "channel" : "futures.ping"
-                            });
-                            let _ = web_socket.write_message(Message::Ping(Vec::from(ping_str.to_string())));
-                            start_ping = new_time;
-                        }
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                    break;
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
+        //链接
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array,
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("币安-期货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+        Ok(())
+    }
+    // //代理
+    // async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>, subscription: Vec<Value>)
+    // {
+    //     info!("走代理-链接成功!开始数据读取");
+    //     let label = self.label.clone();
+    //     /*****订阅***/
+    //     for sub in &subscription {
+    //         trace!("订阅信息:{}", sub.to_string());
+    //         web_socket.write_message(Message::Text(sub.to_string()))
+    //             .unwrap();
+    //     }
+    //     /*****消息溜***/
+    //     let mut start_ping = chrono::Utc::now().timestamp_millis();
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(1)).await;
+    //         let msg = web_socket.read_message();
+    //         match msg {
+    //             Ok(Message::Text(text)) => {
+    //                 // trace!("获取推送:{}",text.clone());
+    //                 let mut res_data = Self::ok_text(label.to_string(), text);
+    //                 res_data.time = get_time_microsecond();
+    //                 if res_data.code == "-200" {
+    //                     trace!("订阅成功:{:?}", res_data.data);
+    //                 } else {
+    //                     // self.sender.send(res_data).await.unwrap();
+    //                     let sender = self.sender.clone();
+    //                     tokio::spawn(async move {
+    //                         sender.send(res_data).await.unwrap();
+    //                     });
+    //                     tokio::spawn(async move {});
+    //
+    //                     //主动ping 服务器
+    //                     let new_time = chrono::Utc::now().timestamp_millis();
+    //                     // let tr = format!("判断-ping {}--{},{},{}",new_time,start_ping,(new_time - start_ping), (new_time - start_ping) > 10000);
+    //                     // trace!(tr);
+    //                     if (new_time - start_ping) > 10000 {
+    //                         let t = chrono::Utc::now().timestamp();
+    //                         let ping_str = serde_json::json!({
+    //                             "time" :t, "channel" : "futures.ping"
+    //                         });
+    //                         let _ = web_socket.write_message(Message::Ping(Vec::from(ping_str.to_string())));
+    //                         start_ping = new_time;
+    //                     }
+    //                 }
+    //             }
+    //             Ok(Message::Ping(s)) => {
+    //                 trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
+    //                 let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
+    //                 trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Pong(s)) => {
+    //                 // trace!("Pong-响应--{:?}", String::from_utf8(s));
+    //                 trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Close(_)) => {
+    //                 // trace!("socket 关闭: ");
+    //                 trace!( "Close-响应");
+    //                 break;
+    //             }
+    //             Err(error) => {
+    //                 // trace!("Error receiving message: {}", error);
+    //                 error!( "Err-响应{}", error);
+    //                 break;
+    //             }
+    //             _ => {}
+    //         }
+    //
+    //         let bool_v1_v = bool_v1.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    //     web_socket.close(None).unwrap();
+    // }
+    //
+    // //非代理
+    // async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
+    //                       subscription: Vec<Value>)
+    // {
+    //     info!("链接成功!开始数据读取");
+    //     let label = self.label.clone();
+    //     /*****订阅***/
+    //     for sub in &subscription {
+    //         trace!("订阅信息:{}", sub.to_string());
+    //         web_socket.write_message(Message::Text(sub.to_string()))
+    //             .unwrap();
+    //     }
+    //     /*****消息溜***/
+    //     let mut start_ping = chrono::Utc::now().timestamp_millis();
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(1)).await;
+    //         let msg = web_socket.read_message();
+    //         match msg {
+    //             Ok(Message::Text(text)) => {
+    //                 // trace!("获取推送:{}",text.clone());
+    //                 let mut res_data = Self::ok_text(label.to_string(), text);
+    //                 res_data.time = get_time_microsecond();
+    //                 if res_data.code == "-200" {
+    //                     trace!("订阅成功:{:?}", res_data.data);
+    //                 } else {
+    //                     // self.sender.send(res_data).await.unwrap();
+    //                     let sender = self.sender.clone();
+    //                     tokio::spawn(async move {
+    //                         sender.send(res_data).await.unwrap();
+    //                     });
+    //                     tokio::spawn(async move {});
+    //
+    //                     //主动ping 服务器
+    //                     let new_time = chrono::Utc::now().timestamp_millis();
+    //                     // let tr = format!("判断-ping {}--{},{},{}",new_time,start_ping,(new_time - start_ping), (new_time - start_ping) > 10000);
+    //                     // trace!(tr);
+    //                     if (new_time - start_ping) > 10000 {
+    //                         let t = chrono::Utc::now().timestamp();
+    //                         let ping_str = serde_json::json!({
+    //                             "time" :t, "channel" : "futures.ping"
+    //                         });
+    //                         let _ = web_socket.write_message(Message::Ping(Vec::from(ping_str.to_string())));
+    //                         start_ping = new_time;
+    //                     }
+    //                 }
+    //             }
+    //             Ok(Message::Ping(s)) => {
+    //                 trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
+    //                 let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
+    //                 trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Pong(s)) => {
+    //                 // trace!("Pong-响应--{:?}", String::from_utf8(s));
+    //                 trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Close(_)) => {
+    //                 // trace!("socket 关闭: ");
+    //                 trace!( "Close-响应");
+    //                 break;
+    //             }
+    //             Err(error) => {
+    //                 // trace!("Error receiving message: {}", error);
+    //                 error!( "Err-响应{}", error);
+    //                 break;
+    //             }
+    //             _ => {}
+    //         }
+    //
+    //         let bool_v1_v = bool_v1.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    //     web_socket.close(None).unwrap();
+    // }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-201" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
+            _ => None
         }
-        web_socket.close(None).unwrap();
     }
-
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
+    }
     //数据解析
-    pub fn ok_text(label: String, text: String) -> ResponseData
+    pub fn ok_text(text: String) -> ResponseData
     {
         // trace!("原始数据:{}", text);
-        let mut res_data = ResponseData::new(label.to_string(), "200".to_string(), "success".to_string(), "".to_string());
+        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("error").is_some() {
             let message = json_value["error"]["message"].as_str().unwrap().to_string();
             let mes = message.trim_end_matches('\n');
 
-            // let mes_json_value: serde_json::Value = serde_json::from_str(mes).unwrap();
-            // // trace!("错误信息:{}", mes_json_value.to_string());
             res_data.code = json_value["error"]["code"].to_string();
             res_data.message = mes.to_string();
         } else if json_value["result"]["status"].as_str() == Option::from("success") {//订阅返回
-            res_data.code = "-200".to_string();
+            res_data.code = "-201".to_string();
             res_data.data = text;
         } else {
             res_data.channel = format!("{}", json_value["channel"].as_str().unwrap());

+ 36 - 0
exchanges/src/kucoin_spot_rest.rs

@@ -75,6 +75,42 @@ impl KucoinSpotRest {
         ).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!({

+ 179 - 297
exchanges/src/kucoin_spot_ws.rs

@@ -1,37 +1,36 @@
-use std::collections::{BTreeMap, HashSet};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+use std::collections::BTreeMap;
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::Duration;
-use tokio::sync::mpsc::Sender;
+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::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
+
 use crate::kucoin_spot_rest::KucoinSpotRest;
-use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
 use crate::utils::get_time_microsecond;
 
-
-pub enum KucoinWsType {
+//类型
+pub enum KucoinSpotWsType {
     Public,
     Private,
 }
 
 #[derive(Debug)]
 #[derive(Clone)]
-pub struct KucoinWsParam {
+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 KucoinSubscribeType {
+//订阅频道
+#[derive(Clone)]
+pub enum KucoinSpotSubscribeType {
     PuSpotMarketLevel2Depth50,
     PuMarketMatch,
     PuMarketTicker,
@@ -40,90 +39,103 @@ pub enum KucoinSubscribeType {
     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 {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
+    //类型
+    label: String,
+    //地址
+    address_url: String,
     //代理信息
-    // login_param: BTreeMap<String, String>,
+    login_param: Option<KucoinSpotLogin>,
     //登陆数据
-    ws_param: KucoinWsParam,
-    //kuconis特殊参数
+    ws_param: KucoinSpotWsParam,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
-    subscribe_types: Vec<KucoinSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,     //数据通道
+    //订阅
+    subscribe_types: Vec<KucoinSpotSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl KucoinSpotWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub async fn new(is_colo: bool,
-                     login_param: BTreeMap<String, String>,
-                     ws_type: KucoinWsType,
-                     sender: Sender<ResponseData>,
-    ) -> KucoinSpotWs {
-        return KucoinSpotWs::new_label("default-KucoinSpotWs".to_string(), is_colo, login_param, ws_type, sender).await;
+    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: BTreeMap<String, String>,
-                           ws_type: KucoinWsType,
-                           sender: Sender<ResponseData>,
-    ) -> KucoinSpotWs
-    {
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
+    pub async fn new_label(label: String, is_colo: bool, login_param: Option<KucoinSpotLogin>, ws_type: KucoinSpotWsType) -> KucoinSpotWs {
         /*******公共频道-私有频道数据组装*/
-        let mut ws_param = KucoinWsParam {
+        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;
-        match res_data {
+        let address_url = match res_data {
             Ok(param) => {
-                ws_param = param
+                ws_param = param;
+                format!("{}?token={}", ws_param.ws_url, ws_param.token)
             }
             Err(error) => {
-                error!("-链接地址等参数错误:{:?}", error)
+                error!("-链接地址等参数错误:{:?}", error);
+                "".to_string()
             }
-        }
+        };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
 
-        /*****返回结构体*******/
         KucoinSpotWs {
             label,
-            request_url: "".to_string(),
-            proxy: parsing_detail,
-            // login_param,
+            address_url,
+            login_param,
             ws_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 18,
         }
     }
 
     /*******************************************************************************************************/
     /*****************************************订阅函数********************************************************/
     /*******************************************************************************************************/
-    //手动添加订阅信息
-    pub fn set_subscribe(&mut self, subscribe_types: Vec<KucoinSubscribeType>) {
-        self.subscribe_types.extend(subscribe_types);
-    }
     //根据当前类型获取对应的频道 地址 与 token
-    async fn get_rul_token(ws_type: KucoinWsType, login_param: BTreeMap<String, String>) -> Result<KucoinWsParam, reqwest::Error> {
-        let mut kucoin_exc = KucoinSpotRest::new(false, login_param.clone());
+    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 {
-            KucoinWsType::Public => {
+            KucoinSpotWsType::Public => {
                 kucoin_exc.get_public_token().await
             }
-            KucoinWsType::Private => {
+            KucoinSpotWsType::Private => {
                 kucoin_exc.get_private_token().await
             }
         };
@@ -157,49 +169,62 @@ impl KucoinSpotWs {
             }
 
 
-            Ok(KucoinWsParam { ws_url, token: ws_token, ws_ping_interval, ws_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 async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        let mut symbol_s = b_array.clone();
-        for symbol in symbol_s.iter_mut() {
+    //手动添加订阅信息
+    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 = symbol_s.clone();
-        self.request_url = format!("{}?token={}", self.ws_param.ws_url, self.ws_param.token);
-        info!("走普通通道:{}",  self.request_url);
-        self.run(bool_v1).await;
+        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: KucoinSubscribeType) -> serde_json::Value {
+    pub fn enum_to_string(symbol: String, subscribe_type: KucoinSpotSubscribeType) -> serde_json::Value {
         match subscribe_type {
-            KucoinSubscribeType::PuSpotMarketLevel2Depth50 => {//level2
+            KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50 => {//level2
                 serde_json::json!({
                      "topic": format!("/spotMarket/level2Depth50:{}", symbol),
                      "type": "subscribe",
                      "response": true
                 })
             }
-            KucoinSubscribeType::PuMarketMatch => {//match
+            KucoinSpotSubscribeType::PuMarketMatch => {//match
                 serde_json::json!({
                      "topic": format!("/market/match:{}", symbol),
                      "type": "subscribe",
                      "response": true
                 })
             }
-            KucoinSubscribeType::PuMarketTicker => {//ticker
+            KucoinSpotSubscribeType::PuMarketTicker => {//ticker
                 serde_json::json!({
                      "topic": format!("/market/ticker:{}", symbol),
                      "type": "subscribe",
@@ -207,7 +232,7 @@ impl KucoinSpotWs {
                 })
             }
 
-            KucoinSubscribeType::PrSpotMarketTradeOrders => {//market.tradeOrders
+            KucoinSpotSubscribeType::PrSpotMarketTradeOrders => {//market.tradeOrders
                 serde_json::json!({
                     "type": "subscribe",
                     "topic": "/spotMarket/tradeOrders",
@@ -215,7 +240,7 @@ impl KucoinSpotWs {
                     "response":true,
                 })
             }
-            KucoinSubscribeType::PrAccountBalance => {//account.balance
+            KucoinSpotSubscribeType::PrAccountBalance => {//account.balance
                 serde_json::json!({
                     "type": "subscribe",
                     "topic": "/account/balance",
@@ -225,7 +250,7 @@ impl KucoinSpotWs {
             }
         }
     }
-    //组装订阅数据
+    //订阅信息生成
     pub fn get_subscription(&self) -> Vec<String> {
         let mut array = vec![];
         for symbol in &self.symbol_s {
@@ -239,242 +264,99 @@ impl KucoinSpotWs {
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&self, bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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 subscription: Vec<String> = subscription.into_iter().collect::<HashSet<String>>().into_iter().collect();
-        loop {
-            tokio::time::sleep(Duration::from_millis(5000)).await;
-            trace!("要连接咯~~!!{}", self.request_url);
-            //币安-登陆流程-rest请求获取k然后拿到 key 拼接地址
-            // if self.is_login { //暂时没看到有订阅的频道需要登陆 所以暂时不做
-            // }
-
-            let request_url = Url::parse(self.request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                let _ = match connect_with_proxy(request_url.clone(),
-                                                 proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        error!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                let _ = match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        error!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
-
-            let bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-    }
-
-    //代理
-    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>,
-                                subscription: Vec<String>)
-    {
-        info!("走代理-链接成功!开始数据读取");
-        info!(?subscription);
+        let address_url = self.address_url.clone();
         let label = self.label.clone();
-        /*****消息溜***/
-        let mut ping_interval = chrono::Utc::now().timestamp_millis();
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    //心跳-一定时间间隔发送心跳
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    // trace!("--心跳-{}-{}-{}-{}-{}", get_time, ping_interval
-                    //          , (get_time - ping_interval), self.ws_param.ws_ping_interval, (get_time - ping_interval) >= self.ws_param.ws_ping_interval);
-                    if (get_time - ping_interval) >= self.ws_param.ws_ping_interval {
-                        web_socket.write_message(Message::Ping(Vec::from("ping")))
-                            .unwrap();
-                        trace!("--发送心跳-ping");
-                        ping_interval = get_time;
-                        ping_timeout = get_time;
-                    } else if (get_time - ping_timeout) > (self.ws_param.ws_ping_timeout + self.ws_param.ws_ping_interval) {
-                        //心跳超时-发送心跳之后 一定时间没有响应
-                        trace!("--心跳相应超时-重连");
-                        break;
-                    }
+        let heartbeat_time = self.heartbeat_time.clone();
 
-                    // trace!("获取推送:{}",text.clone());
-                    // trace!(stdout, "Text-响应--{:?}", text.clone()).unwrap();
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    // trace!("获取推送:{:?}", res_data);
-                    if res_data.code == "-200" {//表示链接成功
-                        for sub in &subscription {
-                            trace!("--发起订阅:{:?}", sub);
-                            web_socket.write_message(Message::Text(sub.parse().unwrap()))
-                                .unwrap();
-                        }
-                    } else if res_data.code == "-201" {
-                        trace!("订阅成功:{:?}", res_data);
-                    } else if res_data.code == "-202" {
-                        trace!("无用数据:{:?}", res_data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                    ping_timeout = chrono::Utc::now().timestamp_millis();
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = write_tx_am.clone();
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time).await;
+            trace!("线程-异步心跳-结束");
+        });
 
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
+        //设置订阅
+        let subscribe_array = subscription.clone();
+        if login_is {
+            //登录相关
         }
-        web_socket.close(None).unwrap();
-    }
+        // 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();
+        //     }
+        // });
 
-    //非代理
-    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
-                          subscription: Vec<String>)
-    {
-        info!("链接成功!开始数据读取");
-        let label = self.label.clone();
-        /*****消息溜***/
-        let mut ping_interval = chrono::Utc::now().timestamp_millis();
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    //心跳-一定时间间隔发送心跳
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    // trace!("--心跳-{}-{}-{}-{}-{}", get_time, ping_interval
-                    //          , (get_time - ping_interval), self.ws_param.ws_ping_interval, (get_time - ping_interval) >= self.ws_param.ws_ping_interval);
-                    if (get_time - ping_interval) >= self.ws_param.ws_ping_interval {
-                        web_socket.write_message(Message::Ping(Vec::from("ping")))
-                            .unwrap();
-                        trace!("--发送心跳-ping");
-                        ping_interval = get_time;
-                        ping_timeout = get_time;
-                    } else if (get_time - ping_timeout) > (self.ws_param.ws_ping_timeout + self.ws_param.ws_ping_interval) {
-                        //心跳超时-发送心跳之后 一定时间没有响应
-                        trace!("--心跳相应超时-重连");
-                        break;
-                    }
 
-                    // trace!("获取推送:{}",text.clone());
-                    // trace!(stdout, "Text-响应--{:?}", text.clone()).unwrap();
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    // trace!("获取推送:{:?}", res_data);
-                    if res_data.code == "-200" {//表示链接成功
-                        for sub in &subscription {
-                            trace!("--发起订阅:{:?}", sub);
-                            web_socket.write_message(Message::Text(sub.parse().unwrap()))
-                                .unwrap();
-                        }
-                    } else if res_data.code == "-201" {
-                        trace!("订阅成功:{:?}", res_data);
-                    } else if res_data.code == "-202" {
-                        trace!("无用数据:{:?}", res_data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                    ping_timeout = chrono::Utc::now().timestamp_millis();
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
+        //1 链接
+
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array.clone(),
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("kucoin-现货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-201" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
+            "-202" => {
+                trace!("未知解析:{:?}", response_data);
+                None
+            }
+            _ => None
         }
-        web_socket.close(None).unwrap();
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
     }
 
     //数据解析
-    pub fn ok_text(lable: String, text: String) -> ResponseData
+    pub fn ok_text(text: String) -> ResponseData
     {
-        let mut res_data = ResponseData::new(lable, "200".to_string(), "success".to_string(), "".to_string());
+        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") {
@@ -501,7 +383,7 @@ impl KucoinSpotWs {
                 res_data.data = json_value["data"].to_string();
             }
         } else {
-            res_data.code = "-1".to_string();
+            res_data.code = "-202".to_string();
             res_data.message = "未知解析".to_string();
         }
         res_data

+ 37 - 1
exchanges/src/kucoin_swap_rest.rs

@@ -71,6 +71,43 @@ impl KucoinSwapRest {
         ).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!({
@@ -522,7 +559,6 @@ impl KucoinSwapRest {
         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),

+ 189 - 301
exchanges/src/kucoin_swap_ws.rs

@@ -1,37 +1,37 @@
-use std::collections::{BTreeMap, HashSet};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+use std::collections::BTreeMap;
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::time::Duration;
-use tokio::sync::mpsc::Sender;
+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::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
+
 use crate::kucoin_swap_rest::KucoinSwapRest;
-use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::socket_tool::{AbstractWsMode, HeartbeatType};
 use crate::utils::get_time_microsecond;
 
-
-pub enum KucoinWsType {
+//类型
+pub enum KucoinSwapWsType {
     Public,
     Private,
 }
 
+
 #[derive(Debug)]
 #[derive(Clone)]
-pub struct KucoinWsParam {
+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 KucoinSubscribeType {
+//订阅频道
+#[derive(Clone)]
+pub enum KucoinSwapSubscribeType {
     PuContractMarketLevel2Depth50,
     PuContractMarketExecution,
     PuContractMarkettickerV2,
@@ -42,90 +42,105 @@ pub enum KucoinSubscribeType {
     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 {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
-    //代理信息
-    // login_param: BTreeMap<String, String>,
+    //类型
+    label: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<KucoinSwapLogin>,
     //登陆数据
-    ws_param: KucoinWsParam,
-    //kuconis特殊参数
+    ws_param: KucoinSwapWsParam,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
-    subscribe_types: Vec<KucoinSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,     //数据通道
+    //订阅
+    subscribe_types: Vec<KucoinSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl KucoinSwapWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub async fn new(is_colo: bool,
-                     login_param: BTreeMap<String, String>,
-                     ws_type: KucoinWsType,
-                     sender: Sender<ResponseData>,
-    ) -> KucoinSwapWs {
-        return KucoinSwapWs::new_label("default-KucoinSwapWs".to_string(), is_colo, login_param, ws_type, sender).await;
+    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: BTreeMap<String, String>,
-                           ws_type: KucoinWsType,
-                           sender: Sender<ResponseData>,
-    ) -> KucoinSwapWs
-    {
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
+    pub async fn new_label(label: String, is_colo: bool, login_param: Option<KucoinSwapLogin>, ws_type: KucoinSwapWsType) -> KucoinSwapWs {
         /*******公共频道-私有频道数据组装*/
-        let mut ws_param = KucoinWsParam {
+        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 = KucoinSwapWs::get_rul_token(ws_type, login_param.clone()).await;
-        match res_data {
+
+        /*******公共频道-私有频道数据组装*/
+        let res_data = Self::get_rul_token(ws_type, login_param.clone()).await;
+        let address_url = match res_data {
             Ok(param) => {
-                ws_param = param
+                ws_param = param;
+                format!("{}?token={}", ws_param.ws_url, ws_param.token)
             }
             Err(error) => {
-                error!("-链接地址等参数错误:{:?}", error)
+                error!("-链接地址等参数错误:{:?}", error);
+                "".to_string()
             }
-        }
+        };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
 
-        /*****返回结构体*******/
         KucoinSwapWs {
             label,
-            request_url: "".to_string(),
-            proxy: parsing_detail,
-            // login_param,
+            address_url,
+            login_param,
             ws_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 18,
         }
     }
 
     /*******************************************************************************************************/
     /*****************************************订阅函数********************************************************/
     /*******************************************************************************************************/
-    //手动添加订阅信息
-    pub fn set_subscribe(&mut self, subscribe_types: Vec<KucoinSubscribeType>) {
-        self.subscribe_types.extend(subscribe_types);
-    }
     //根据当前类型获取对应的频道 地址 与 token
-    async fn get_rul_token(ws_type: KucoinWsType, login_param: BTreeMap<String, String>) -> Result<KucoinWsParam, reqwest::Error> {
-        let mut kucoin_exc = KucoinSwapRest::new(false, login_param.clone());
+    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 {
-            KucoinWsType::Public => {
+            KucoinSwapWsType::Public => {
                 kucoin_exc.get_public_token().await
             }
-            KucoinWsType::Private => {
+            KucoinSwapWsType::Private => {
                 kucoin_exc.get_private_token().await
             }
         };
@@ -159,49 +174,72 @@ impl KucoinSwapWs {
             }
 
 
-            Ok(KucoinWsParam { ws_url, token: ws_token, ws_ping_interval, ws_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 async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        self.symbol_s = b_array.clone();
-        self.request_url = format!("{}?token={}", self.ws_param.ws_url, self.ws_param.token);
-        info!("走普通通道:{}",  self.request_url);
-        self.run(bool_v1).await;
+    //手动添加订阅信息
+    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: KucoinSubscribeType) -> serde_json::Value {
+    pub fn enum_to_string(symbol: String, subscribe_type: KucoinSwapSubscribeType) -> serde_json::Value {
         match subscribe_type {
-            KucoinSubscribeType::PuContractMarketLevel2Depth50 => {//level2
+            KucoinSwapSubscribeType::PuContractMarketLevel2Depth50 => {//level2
                 serde_json::json!({
                      "topic": format!("/contractMarket/level2Depth50:{}", symbol),
                      "type": "subscribe",
                      "response": true
                 })
             }
-            KucoinSubscribeType::PuContractMarketExecution => {//match
+            KucoinSwapSubscribeType::PuContractMarketExecution => {//match
                 serde_json::json!({
                      "topic": format!("/contractMarket/execution:{}", symbol),
                      "type": "subscribe",
                      "response": true
                 })
             }
-            KucoinSubscribeType::PuContractMarkettickerV2 => {//tickerV2
+            KucoinSwapSubscribeType::PuContractMarkettickerV2 => {//tickerV2
                 serde_json::json!({
                      "topic": format!("/contractMarket/tickerV2:{}", symbol),
                      "type": "subscribe",
                      "response": true
                 })
             }
-            KucoinSubscribeType::PrContractAccountWallet => {//orderMargin.change
+            KucoinSwapSubscribeType::PrContractAccountWallet => {//orderMargin.change
                 serde_json::json!({
                     "type": "subscribe",
                     "topic": "/contractAccount/wallet",
@@ -209,7 +247,7 @@ impl KucoinSwapWs {
                     "response":true,
                 })
             }
-            KucoinSubscribeType::PrContractPosition => {//position.change
+            KucoinSwapSubscribeType::PrContractPosition => {//position.change
                 serde_json::json!({
                     "type": "subscribe",
                     "topic": format!("/contract/position:{}", symbol),
@@ -217,7 +255,7 @@ impl KucoinSwapWs {
                     "response":true,
                 })
             }
-            KucoinSubscribeType::PrContractMarketTradeOrdersSys => {//orderChange
+            KucoinSwapSubscribeType::PrContractMarketTradeOrdersSys => {//orderChange
                 serde_json::json!({
                     "type": "subscribe",
                     "topic": format!("/contractMarket/tradeOrders"),
@@ -225,7 +263,7 @@ impl KucoinSwapWs {
                     "response":true,
                 })
             }
-            KucoinSubscribeType::PrContractMarketTradeOrders => {//symbolOrderChange
+            KucoinSwapSubscribeType::PrContractMarketTradeOrders => {//symbolOrderChange
                 serde_json::json!({
                     "type": "subscribe",
                     "topic": format!("/contractMarket/tradeOrders:{}", symbol),
@@ -235,7 +273,7 @@ impl KucoinSwapWs {
             }
         }
     }
-    //组装订阅数据
+    //订阅信息生成
     pub fn get_subscription(&self) -> Vec<String> {
         let mut array = vec![];
         for symbol in &self.symbol_s {
@@ -249,247 +287,97 @@ impl KucoinSwapWs {
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&self, bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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 subscription: Vec<String> = subscription.into_iter().collect::<HashSet<String>>().into_iter().collect();
-        loop {
-            tokio::time::sleep(Duration::from_millis(5000)).await;
-            trace!("要连接咯~~!!{}", self.request_url);
-            //币安-登陆流程-rest请求获取k然后拿到 key 拼接地址
-            // if self.is_login { //暂时没看到有订阅的频道需要登陆 所以暂时不做
-            // }
-
-            let request_url = Url::parse(self.request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                let _ = match connect_with_proxy(request_url.clone(),
-                                                 proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        error!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                let _ = match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        error!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
+        let address_url = self.address_url.clone();
+        let label = self.label.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
 
-            let bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = write_tx_am.clone();
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time).await;
+            trace!("线程-异步心跳-结束");
+        });
+
+
+        //设置订阅
+        let subscribe_array = subscription.clone();
+        if login_is {
+            //登录相关
         }
-    }
 
-    //代理
-    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>,
-                                subscription: Vec<String>)
-    {
-        info!("走代理-链接成功!开始数据读取");
-        let label = self.label.clone();
-        /*****消息溜***/
-        let mut ping_interval = chrono::Utc::now().timestamp_millis();
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    //心跳-一定时间间隔发送心跳
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    // trace!("--心跳-{}-{}-{}-{}-{}", get_time, ping_interval
-                    //          , (get_time - ping_interval), self.ws_param.ws_ping_interval, (get_time - ping_interval) >= self.ws_param.ws_ping_interval);
-                    if (get_time - ping_interval) >= self.ws_param.ws_ping_interval {
-                        web_socket.write_message(Message::Ping(Vec::from("ping")))
-                            .unwrap();
-                        trace!("--发送心跳-ping");
-                        ping_interval = get_time;
-                        ping_timeout = get_time;
-                    } else if (get_time - ping_timeout) > (self.ws_param.ws_ping_timeout + self.ws_param.ws_ping_interval) {
-                        //心跳超时-发送心跳之后 一定时间没有响应
-                        trace!("--心跳相应超时-重连");
-                        break;
-                    }
-
-                    trace!("获取推送:{}",text.clone());
-                    // trace!(stdout, "Text-响应--{:?}", text.clone()).unwrap();
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    // trace!("获取推送:{:?}", res_data);
-                    if res_data.code == "-200" {//表示链接成功
-                        for sub in &subscription {
-                            trace!("--发起订阅:{:?}", sub);
-                            web_socket.write_message(Message::Text(sub.parse().unwrap()))
-                                .unwrap();
-                        }
-                    } else if res_data.code == "-201" {
-                        trace!("订阅成功:{:?}", res_data);
-                    } else if res_data.code == "-202" {
-                        trace!("无用数据:{:?}", res_data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                    ping_timeout = chrono::Utc::now().timestamp_millis();
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
 
+        //1 链接
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-        web_socket.close(None).unwrap();
-    }
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array,
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("kucoin-期货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
-    //非代理
-    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
-                          subscription: Vec<String>)
-    {
-        info!("链接成功!开始数据读取");
-        let label = self.label.clone();
-        /*****消息溜***/
-        let mut ping_interval = chrono::Utc::now().timestamp_millis();
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    //心跳-一定时间间隔发送心跳
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    // trace!("--心跳-{}-{}-{}-{}-{}", get_time, ping_interval
-                    //          , (get_time - ping_interval), self.ws_param.ws_ping_interval, (get_time - ping_interval) >= self.ws_param.ws_ping_interval);
-                    if (get_time - ping_interval) >= self.ws_param.ws_ping_interval {
-                        web_socket.write_message(Message::Ping(Vec::from("ping")))
-                            .unwrap();
-                        trace!("--发送心跳-ping");
-                        ping_interval = get_time;
-                        ping_timeout = get_time;
-                    } else if (get_time - ping_timeout) > (self.ws_param.ws_ping_timeout + self.ws_param.ws_ping_interval) {
-                        //心跳超时-发送心跳之后 一定时间没有响应
-                        trace!("--心跳相应超时-重连");
-                        break;
-                    }
-
-                    trace!("获取推送:{}",text.clone());
-                    // trace!(stdout, "Text-响应--{:?}", text.clone()).unwrap();
-                    let mut res_data = Self::ok_text(label.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    // trace!("获取推送:{:?}", res_data);
-                    if res_data.code == "-200" {//表示链接成功
-                        for sub in &subscription {
-                            trace!("--发起订阅:{:?}", sub);
-                            web_socket.write_message(Message::Text(sub.parse().unwrap()))
-                                .unwrap();
-                        }
-                    } else if res_data.code == "-201" {
-                        trace!("订阅成功:{:?}", res_data);
-                    } else if res_data.code == "-202" {
-                        trace!("无用数据:{:?}", res_data);
-                    } else {
-                        let sender = self.sender.clone();
-                        tokio::spawn(async move {
-                            sender.send(res_data).await.unwrap();
-                        });
-                        tokio::spawn(async move {});
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!("Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!("Pong-响应--{:?}", String::from_utf8(s.clone()));
-                    ping_timeout = chrono::Utc::now().timestamp_millis();
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(error) => {
-                    // trace!("Error receiving message: {}", error);
-                    error!( "Err-响应{}", error);
-                    break;
-                }
-                _ => {}
-            }
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-201" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
+            "-202" => {
+                trace!("未知解析:{:?}", response_data);
+                None
+            }
+            _ => None
         }
-        web_socket.close(None).unwrap();
     }
-
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
+    }
     //数据解析
-    pub fn ok_text(lable: String, text: String) -> ResponseData
+    pub fn ok_text(text: String) -> ResponseData
     {
-        let mut res_data = ResponseData::new(lable, "200".to_string(), "success".to_string(), "".to_string());
+        // 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["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();
@@ -509,7 +397,7 @@ impl KucoinSwapWs {
                 res_data.data = json_value["data"].to_string();
             }
         } else {
-            res_data.code = "-1".to_string();
+            res_data.code = "-202".to_string();
             res_data.message = "未知解析".to_string();
         }
         res_data

+ 107 - 6
exchanges/src/okx_swap_rest.rs

@@ -31,9 +31,9 @@ impl OkxSwapRest {
 
     pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> OkxSwapRest
     {
-        return OkxSwapRest::new_lable("default-OkxSwapRest".to_string(), is_colo, login_param);
+        return OkxSwapRest::new_label("default-OkxSwapRest".to_string(), is_colo, login_param);
     }
-    pub fn new_lable(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> OkxSwapRest {
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> OkxSwapRest {
         let base_url = if is_colo {
             "https://www.okx.com".to_string()
         } else {
@@ -122,7 +122,7 @@ impl OkxSwapRest {
     //获取单个产品行情信息
     pub async fn get_ticker(&mut self, symbol: String) -> ResponseData {
         let params = serde_json::json!({
-            "instId":format!("{}-SWAP",symbol)
+            "instId":symbol
          });
         let data = self.request("GET".to_string(),
                                 "/api/v5".to_string(),
@@ -176,12 +176,37 @@ impl OkxSwapRest {
         data
     }
 
+    //获取成交明细(近三个月)
+    pub async fn get_trade_fills_history(&mut self, inst_id: String, begin: String, end: String, limit: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "instType": "SWAP",
+            "instId":inst_id,
+            "limit":100,
+         });
+
+        if begin.len() > 0 {
+            params["begin"] = serde_json::json!(begin);
+        }
+        if end.len() > 0 {
+            params["end"] = serde_json::json!(end);
+        }
+        if limit.len() > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/trade/fills-history".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
 
     //合约-下单
     pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
         let data = self.request("POST".to_string(),
                                 "/api/v5".to_string(),
-                                "/sprd/order".to_string(),
+                                "/trade/order".to_string(),
                                 true,
                                 params.to_string(),
         ).await;
@@ -235,6 +260,82 @@ impl OkxSwapRest {
         data
     }
 
+    //获取历史订单记录(近七天)
+    pub async fn get_orders_history(&mut self,
+                                    sprd_id: String,
+                                    ord_type: String,
+                                    state: String,
+                                    begin: String,
+                                    end: String,
+                                    limit: String,
+    ) -> ResponseData {
+        let mut params = serde_json::json!({
+         });
+        if sprd_id.len() > 0 {
+            params["sprdId"] = serde_json::json!(sprd_id);
+        }
+        if ord_type.len() > 0 {
+            params["ordType"] = serde_json::json!(ord_type);
+        }
+        if state.len() > 0 {
+            params["state"] = serde_json::json!(state);
+        }
+        if begin.len() > 0 {
+            params["begin"] = serde_json::json!(begin);
+        }
+        if end.len() > 0 {
+            params["end"] = serde_json::json!(end);
+        }
+        if limit.len() > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/sprd/orders-history".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //获取历史成交数据(近七天)
+    pub async fn get_trades(&mut self,
+                            sprd_id: String,
+                            trade_id: String,
+                            ord_id: String,
+                            begin: String,
+                            end: String,
+                            limit: String,
+    ) -> ResponseData {
+        let mut params = serde_json::json!({
+         });
+        if sprd_id.len() > 0 {
+            params["sprdId"] = serde_json::json!(sprd_id);
+        }
+        if trade_id.len() > 0 {
+            params["tradeId"] = serde_json::json!(trade_id);
+        }
+        if ord_id.len() > 0 {
+            params["ordId"] = serde_json::json!(ord_id);
+        }
+        if begin.len() > 0 {
+            params["begin"] = serde_json::json!(begin);
+        }
+        if end.len() > 0 {
+            params["end"] = serde_json::json!(end);
+        }
+        if limit.len() > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/sprd/trades".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/
@@ -421,7 +522,7 @@ impl OkxSwapRest {
         match result {
             Ok(res_data) => {
                 if res_data.code != "200" {
-                    trace!("不等于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();
@@ -432,7 +533,7 @@ impl OkxSwapRest {
                                                   format!("请求地址:{},请求参数:{}", base_url, params));
                     error
                 } else {
-                    trace!("等于200");
+                    // trace!("等于200");
                     let body: String = res_data.data;
                     let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
 

+ 359 - 264
exchanges/src/okx_swap_ws.rs

@@ -1,40 +1,37 @@
-use std::collections::{BTreeMap};
-use std::{thread};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
 use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::atomic::AtomicBool;
 use std::time::Duration;
+
 use chrono::Utc;
-use serde_json::{json, Value};
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use ring::hmac;
-use tokio::sync::mpsc::Sender;
-use tracing::{error, info, trace};
-use crate::{proxy};
-use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
-use tungstenite::{connect, Message, WebSocket};
-use tungstenite::protocol::WebSocketConfig;
-use url::Url;
-use crate::proxy::ParsingDetail;
+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};
 use crate::utils::get_time_microsecond;
 
-pub enum OkxWsType {
+//类型
+pub enum OkxSwapWsType {
     //订阅频道类型
     Public,
     Private,
     Business,
 }
 
-
-#[derive(Clone)]                        //订阅枚举
-pub enum OkxSubscribeType {
+//订阅频道
+#[derive(Clone)]
+pub enum OkxSwapSubscribeType {
     PuIndexTickers,
     PuBooks5,
     Putrades,
     PuBooks50L2tbt,
-    //
+
     BuIndexCandle30m,
-    //
+
     PrBalanceAndPosition,
     PrAccount(String),
     PrOrders,
@@ -42,70 +39,65 @@ pub enum OkxSubscribeType {
 
 }
 
+//账号信息
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct OkxSwapLogin {
+    pub api_key: String,
+    pub secret_key: String,
+    pub passphrase: String,
+}
+
 #[derive(Clone)]
 pub struct OkxSwapWs {
-    pub label: String,
-    request_url: String,
-    //实际ws 链接地址
-    proxy: ParsingDetail,
+    //类型
+    label: String,
+    //地址
+    address_url: String,
     //账号信息
-    login_param: BTreeMap<String, String>,
-    //kuconis特殊参数
+    login_param: Option<OkxSwapLogin>,
+    //币对
     symbol_s: Vec<String>,
-    //订阅币对
-    subscribe_types: Vec<OkxSubscribeType>,
-    //订阅信息
-    sender: Sender<ResponseData>,     //数据通道
+    //订阅
+    subscribe_types: Vec<OkxSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
 }
 
 impl OkxSwapWs {
     /*******************************************************************************************************/
     /*****************************************获取一个对象****************************************************/
     /*******************************************************************************************************/
-    pub fn new(is_colo: bool,
-               login_param: BTreeMap<String, String>,
-               ws_type: OkxWsType,
-               sender: Sender<ResponseData>,
-    ) -> OkxSwapWs
-    {
-        return OkxSwapWs::new_label("default-OkxSwapWs".to_string(), is_colo, login_param, ws_type, sender);
+    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: BTreeMap<String, String>,
-                     ws_type: OkxWsType,
-                     sender: Sender<ResponseData>,
-    ) -> OkxSwapWs
-    {
-        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-
+    pub fn new_label(label: String, is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
         /*******公共频道-私有频道数据组装*/
-        let request_url = match ws_type {
-            OkxWsType::Public => {
+        let address_url = match ws_type {
+            OkxSwapWsType::Public => {
                 "wss://ws.okx.com:8443/ws/v5/public".to_string()
             }
-            OkxWsType::Private => {
+            OkxSwapWsType::Private => {
                 "wss://ws.okx.com:8443/ws/v5/private".to_string()
             }
-            OkxWsType::Business => {
+            OkxSwapWsType::Business => {
                 "wss://ws.okx.com:8443/ws/v5/business".to_string()
             }
         };
 
         if is_colo {
-            info!("开启高速(未配置,走普通:{})通道",request_url);
+            info!("开启高速(未配置,走普通:{})通道",address_url);
         } else {
-            info!("走普通通道:{}",request_url);
+            info!("走普通通道:{}",address_url);
         }
         /*****返回结构体*******/
         OkxSwapWs {
             label,
-            request_url,
-            proxy: parsing_detail,
+            address_url,
             login_param,
             symbol_s: vec![],
             subscribe_types: vec![],
-            sender,
+            heartbeat_time: 1000 * 5,
         }
     }
 
@@ -113,81 +105,98 @@ impl OkxSwapWs {
     /*****************************************订阅函数********************************************************/
     /*******************************************************************************************************/
     //手动添加订阅信息
-    pub fn set_subscribe(&mut self, subscribe_types: Vec<OkxSubscribeType>) {
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<OkxSwapSubscribeType>) {
         self.subscribe_types.extend(subscribe_types);
     }
-    //自定义
-    pub async fn custom_subscribe(&mut self, bool_v1: Arc<AtomicBool>, b_array: Vec<String>)
-    {
-        let mut symbol_s = b_array.clone();
-        for symbol in symbol_s.iter_mut() {
-            // 大写
+    //手动添加币对
+    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 = symbol_s;
-        self.run(bool_v1).await;
+        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: OkxSubscribeType) -> Value {
+    pub fn enum_to_string(symbol: String, subscribe_type: OkxSwapSubscribeType) -> Value {
         match subscribe_type {
-            OkxSubscribeType::PuIndexTickers => {
+            OkxSwapSubscribeType::PuIndexTickers => {
                 json!({
                     "channel":"index-tickers",
                     "instId":symbol
                 })
             }
 
-            OkxSubscribeType::PuBooks5 => {
+            OkxSwapSubscribeType::PuBooks5 => {
                 json!({
                     "channel":"books5",
                     "instId":symbol
                 })
             }
-            OkxSubscribeType::Putrades => {
+            OkxSwapSubscribeType::Putrades => {
                 json!({
                     "channel":"trades",
                     "instId":symbol
                 })
             }
 
-            OkxSubscribeType::BuIndexCandle30m => {
+            OkxSwapSubscribeType::BuIndexCandle30m => {
                 json!({
                     "channel":"index-candle30m",
                     "instId":symbol
                 })
             }
 
-            OkxSubscribeType::PrAccount(ccy) => {
+            OkxSwapSubscribeType::PrAccount(ccy) => {
                 json!({
                     "channel":"account",
                     "ccy":ccy
                 })
             }
-            OkxSubscribeType::PuBooks50L2tbt => {
+            OkxSwapSubscribeType::PuBooks50L2tbt => {
                 json!({
                     "channel":"books50-l2-tbt",
                     "instId":symbol
                 })
             }
-            OkxSubscribeType::PrBalanceAndPosition => {
+            OkxSwapSubscribeType::PrBalanceAndPosition => {
                 json!({
                     "channel":"balance_and_position"
                 })
             }
-            OkxSubscribeType::PrOrders => {
+            OkxSwapSubscribeType::PrOrders => {
                 json!({
                     "channel":"orders",
                     "instType":"SWAP",
                     "instFamily":symbol
                 })
             }
-            OkxSubscribeType::PrPositions => {
+            OkxSwapSubscribeType::PrPositions => {
                 json!({
                     "channel":"positions",
                     "instType":"SWAP",
@@ -195,7 +204,7 @@ impl OkxSwapWs {
             }
         }
     }
-    //组装订阅数据
+    //订阅信息生成
     pub fn get_subscription(&self) -> String {
         let mut args = vec![];
         for symbol in &self.symbol_s {
@@ -209,27 +218,25 @@ impl OkxSwapWs {
             "args": args
         });
 
-        trace!("订阅信息:{}", str.to_string());
+        // trace!("订阅信息:{}", str.to_string());
 
         str.to_string()
     }
-
-    //组装登陆数据
-    fn log_in_to_str(&self) -> 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();
-        for (key, value) in &self.login_param {
-            if key == "access_key" {
-                access_key = value.parse().unwrap();
-            } else if key == "secret_key" {
-                secret_key = value.parse().unwrap();
-            } else if key == "pass_key" {
-                passphrase = value.parse().unwrap();
-            }
+
+
+        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();
             // 时间戳 + 请求类型+ 请求参数字符串
@@ -256,199 +263,285 @@ impl OkxSwapWs {
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&mut self, bool_v1: Arc<AtomicBool>)
+    //链接
+    pub async fn ws_connect_async(&mut self,
+                                  bool_v1: 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();
-        loop {
-            trace!("要连接咯~~!!{}", self.request_url);
+        let address_url = self.address_url.clone();
+        let label = self.label.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
 
-            let request_url = Url::parse(self.request_url.as_str()).unwrap();
-            //1. 判断是否需要代理,根据代理地址是否存来选择
-            if self.proxy.ip_address.len() > 0 {
-                let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
-                let proxy_address = 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())
-                ), self.proxy.port.parse().unwrap());
-                let websocket_config = Some(WebSocketConfig {
-                    max_send_queue: Some(16),
-                    max_message_size: Some(16 * 1024 * 1024),
-                    max_frame_size: Some(16 * 1024 * 1024),
-                    accept_unmasked_frames: false,
-                });
-                let max_redirects = 5;
-                match connect_with_proxy(request_url.clone(),
-                                         proxy_address, websocket_config, max_redirects) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.proxy_subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        trace!("Can't connect(无法连接): {}", err);
-                    }
-                };
-            } else {
-                match connect(request_url.clone()) {
-                    Ok(ws) => {
-                        let bool_v1_clone = Arc::clone(&bool_v1);
-                        self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
-                    }
-                    Err(err) => {
-                        // 连接失败时执行的操作
-                        trace!("Can't connect(无法连接): {}", err);
-                        // 返回一个默认的 WebSocket 对象或其他适当的值
-                        // 或者根据需要触发 panic 或返回错误信息
-                    }
-                };
-            }
-            trace!("退出来咯");
 
-            let bool_v1_clone = Arc::clone(&bool_v1);
-            let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-    }
+        //心跳-- 方法内部线程启动
+        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!("线程-异步心跳-结束");
+        });
 
-    //代理
-    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>,
-                                subscription: String)
-    {
-        let lable = self.label.clone();
-        /*****登陆***/
-        let login_str = self.log_in_to_str();
-        if login_str != "" {
-            let _ = web_socket.write_message(Message::Text(login_str));
-            thread::sleep(Duration::from_secs(3));
+        //设置订阅
+        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;
+            });
         }
-        /*****订阅***/
-        web_socket.write_message(Message::Text(subscription))
-            .unwrap();
-        /*****消息溜***/
-        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    if (get_time - ping_timeout) >= (1000 * 30) {
-                        trace!("30s 一次主动发送心跳包!");
-                        let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
-                        ping_timeout = get_time;
-                    }
+        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 mut res_data = Self::ok_text(lable.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-201" {
-                        trace!("登陆成功!");
-                    } else if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        self.sender.send(res_data).await.unwrap();
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(e) => {
-                    // trace!("Error receiving message: {}", error);
-                    trace!("Err-响应{}", e);
-                    error!( "Err-响应{}", e);
-                    break;
-                }
-                _ => {}
-            }
+        //链接
+        let t2 = tokio::spawn(async move {
+            trace!("线程-异步链接-开始");
+            AbstractWsMode::ws_connect_async(bool_v1, address_url.clone(),
+                                             label.clone(), subscribe_array,
+                                             write_rx, read_tx,
+                                             Self::message_text,
+                                             Self::message_ping,
+                                             Self::message_pong,
+            ).await.expect("OKX-期货");
+            trace!("线程-异步链接-结束");
+        });
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
 
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
-            }
-        }
-        web_socket.close(None).unwrap();
+        Ok(())
     }
 
-    //非代理
-    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
-                          subscription: String)
-    {
-        let lable = self.label.clone();
-        /*****订阅***/
-        web_socket.write_message(Message::Text(subscription))
-            .unwrap();
-        /*****消息溜***/
-        let mut  ping_timeout = chrono::Utc::now().timestamp_millis();
-        loop {
-            tokio::time::sleep(Duration::from_millis(1)).await;
-            let msg = web_socket.read_message();
-            match msg {
-                Ok(Message::Text(text)) => {
-                    let get_time = chrono::Utc::now().timestamp_millis();
-                    if (get_time - ping_timeout) >= (1000 * 30) {
-                        trace!("30s 一次主动发送心跳包!");
-                        let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
-                        ping_timeout = get_time;
-                    }
-
-                    let mut res_data = Self::ok_text(lable.to_string(), text);
-                    res_data.time = get_time_microsecond();
-                    if res_data.code == "-201" {
-                        trace!("登陆成功!");
-                    } else if res_data.code == "-200" {
-                        trace!("订阅成功:{:?}", res_data.data);
-                    } else {
-                        self.sender.send(res_data).await.unwrap();
-                    }
-                }
-                Ok(Message::Ping(s)) => {
-                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
-                    let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Pong(s)) => {
-                    // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
-                }
-                Ok(Message::Close(_)) => {
-                    // trace!("socket 关闭: ");
-                    trace!( "Close-响应");
-                }
-                Err(e) => {
-                    // trace!("Error receiving message: {}", error);
-                    trace!("Err-响应{}", e);
-                    error!( "Err-响应{}", e);
-                    break;
-                }
-                _ => {}
+    // async fn run(&mut self, bool_v1: Arc<AtomicBool>)
+    // {
+    //     //订阅信息组装
+    //     let subscription = self.get_subscription();
+    //     loop {
+    //         trace!("要连接咯~~!!{}", self.address_url);
+    //
+    //         let address_url = Url::parse(self.address_url.as_str()).unwrap();
+    //         //1. 判断是否需要代理,根据代理地址是否存来选择
+    //         if self.proxy.ip_address.len() > 0 {
+    //             let ip_array: Vec<&str> = self.proxy.ip_address.split(".").collect();
+    //             let proxy_address = 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())
+    //             ), self.proxy.port.parse().unwrap());
+    //             let websocket_config = Some(WebSocketConfig {
+    //                 max_send_queue: Some(16),
+    //                 max_message_size: Some(16 * 1024 * 1024),
+    //                 max_frame_size: Some(16 * 1024 * 1024),
+    //                 accept_unmasked_frames: false,
+    //             });
+    //             let max_redirects = 5;
+    //             match connect_with_proxy(address_url.clone(),
+    //                                      proxy_address, websocket_config, max_redirects) {
+    //                 Ok(ws) => {
+    //                     let bool_v1_clone = Arc::clone(&bool_v1);
+    //                     self.proxy_subscription(bool_v1_clone, ws.0, subscription.clone()).await;
+    //                 }
+    //                 Err(err) => {
+    //                     trace!("Can't connect(无法连接): {}", err);
+    //                 }
+    //             };
+    //         } else {
+    //             match connect(address_url.clone()) {
+    //                 Ok(ws) => {
+    //                     let bool_v1_clone = Arc::clone(&bool_v1);
+    //                     self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
+    //                 }
+    //                 Err(err) => {
+    //                     // 连接失败时执行的操作
+    //                     trace!("Can't connect(无法连接): {}", err);
+    //                     // 返回一个默认的 WebSocket 对象或其他适当的值
+    //                     // 或者根据需要触发 panic 或返回错误信息
+    //                 }
+    //             };
+    //         }
+    //         trace!("退出来咯");
+    //
+    //         let bool_v1_clone = Arc::clone(&bool_v1);
+    //         let bool_v1_v = bool_v1_clone.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    // }
+    //
+    // //代理
+    // async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>,
+    //                             subscription: String)
+    // {
+    //     let lable = self.label.clone();
+    //     /*****登陆***/
+    //     let login_str = self.log_in_to_str();
+    //     if login_str != "" {
+    //         let _ = web_socket.write_message(Message::Text(login_str));
+    //         thread::sleep(Duration::from_secs(3));
+    //     }
+    //     /*****订阅***/
+    //     web_socket.write_message(Message::Text(subscription))
+    //         .unwrap();
+    //     /*****消息溜***/
+    //     let mut ping_timeout = chrono::Utc::now().timestamp_millis();
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(1)).await;
+    //         let msg = web_socket.read_message();
+    //         match msg {
+    //             Ok(Message::Text(text)) => {
+    //                 let get_time = chrono::Utc::now().timestamp_millis();
+    //                 if (get_time - ping_timeout) >= (1000 * 30) {
+    //                     trace!("30s 一次主动发送心跳包!");
+    //                     let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
+    //                     ping_timeout = get_time;
+    //                 }
+    //
+    //                 let mut res_data = Self::ok_text(lable.to_string(), text);
+    //                 res_data.time = get_time_microsecond();
+    //                 if res_data.code == "-201" {
+    //                     trace!("登陆成功!");
+    //                 } else if res_data.code == "-200" {
+    //                     trace!("订阅成功:{:?}", res_data.data);
+    //                 } else {
+    //                     self.sender.send(res_data).await.unwrap();
+    //                 }
+    //             }
+    //             Ok(Message::Ping(s)) => {
+    //                 trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
+    //                 let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
+    //                 trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Pong(s)) => {
+    //                 // trace!("Pong-响应--{:?}", String::from_utf8(s));
+    //                 trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Close(_)) => {
+    //                 // trace!("socket 关闭: ");
+    //                 trace!( "Close-响应");
+    //             }
+    //             Err(e) => {
+    //                 // trace!("Error receiving message: {}", error);
+    //                 trace!("Err-响应{}", e);
+    //                 error!( "Err-响应{}", e);
+    //                 break;
+    //             }
+    //             _ => {}
+    //         }
+    //
+    //         let bool_v1_v = bool_v1.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    //     web_socket.close(None).unwrap();
+    // }
+    //
+    // //非代理
+    // async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
+    //                       subscription: String)
+    // {
+    //     let lable = self.label.clone();
+    //     /*****订阅***/
+    //     web_socket.write_message(Message::Text(subscription))
+    //         .unwrap();
+    //     /*****消息溜***/
+    //     let mut ping_timeout = chrono::Utc::now().timestamp_millis();
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(1)).await;
+    //         let msg = web_socket.read_message();
+    //         match msg {
+    //             Ok(Message::Text(text)) => {
+    //                 let get_time = chrono::Utc::now().timestamp_millis();
+    //                 if (get_time - ping_timeout) >= (1000 * 30) {
+    //                     trace!("30s 一次主动发送心跳包!");
+    //                     let _ = web_socket.write_message(Message::Ping(Vec::from("ping")));
+    //                     ping_timeout = get_time;
+    //                 }
+    //
+    //                 let mut res_data = Self::ok_text(lable.to_string(), text);
+    //                 res_data.time = get_time_microsecond();
+    //                 if res_data.code == "-201" {
+    //                     trace!("登陆成功!");
+    //                 } else if res_data.code == "-200" {
+    //                     trace!("订阅成功:{:?}", res_data.data);
+    //                 } else {
+    //                     self.sender.send(res_data).await.unwrap();
+    //                 }
+    //             }
+    //             Ok(Message::Ping(s)) => {
+    //                 trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
+    //                 let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
+    //                 trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Pong(s)) => {
+    //                 // trace!("Pong-响应--{:?}", String::from_utf8(s));
+    //                 trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
+    //             }
+    //             Ok(Message::Close(_)) => {
+    //                 // trace!("socket 关闭: ");
+    //                 trace!( "Close-响应");
+    //             }
+    //             Err(e) => {
+    //                 // trace!("Error receiving message: {}", error);
+    //                 trace!("Err-响应{}", e);
+    //                 error!( "Err-响应{}", e);
+    //                 break;
+    //             }
+    //             _ => {}
+    //         }
+    //
+    //         let bool_v1_v = bool_v1.load(Ordering::SeqCst);
+    //         if !bool_v1_v {
+    //             break;
+    //         }
+    //     }
+    //     web_socket.close(None).unwrap();
+    // }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let mut response_data = Self::ok_text(text);
+        response_data.time = get_time_microsecond();
+        match response_data.code.as_str() {
+            "200" => Option::from(response_data),
+            "-200" => {
+                trace!("订阅成功:{:?}", response_data);
+                None
             }
-
-            let bool_v1_v = bool_v1.load(Ordering::SeqCst);
-            if !bool_v1_v {
-                break;
+            "-201" => {
+                trace!("登录成功:{:?}", response_data);
+                None
             }
+            _ => None
         }
-        web_socket.close(None).unwrap();
     }
-
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-ping");
+        return None;
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        trace!("服务器响应-pong");
+        return None;
+    }
     //数据解析
-    pub fn ok_text(lable: String, text: String) -> ResponseData
+    pub fn ok_text(text: String) -> ResponseData
     {
         // trace!("元数据:{}",text);
-        let mut res_data = ResponseData::new(lable, "200".to_string(), "success".to_string(), "".to_string());
+        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("event").is_some() {//订阅返回
             if json_value["event"].as_str() == Option::from("login") &&
@@ -457,17 +550,19 @@ impl OkxSwapWs {
                 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());
+                res_data.message = format!("订阅失败:{}", json_value["msg"].to_string());
             } else if json_value["event"].as_str() == Option::from("subscribe") {
                 res_data.code = "-200".to_string();
                 res_data.data = text;
+                res_data.message = format!("订阅成功!");
             } else {
-                res_data.data = text
+                res_data.data = text;
             }
         } 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.reach_time = json_value["data"][0]["ts"].as_str().unwrap().parse().unwrap()
             } else {
                 res_data.data = text;
                 res_data.channel = "未知频道".to_string();

+ 53 - 1
exchanges/src/proxy.rs

@@ -1,4 +1,18 @@
 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)]
@@ -9,6 +23,44 @@ pub struct ParsingDetail {
 }
 
 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 }
@@ -28,7 +80,7 @@ impl ParsingDetail {
                 parsing_detail
             }
             Err(_) => {
-                //trace!("环境变量读取失败:'proxy_address'");
+                trace!("环境变量读取失败:'proxy_address'");
                 let parsing_detail = ParsingDetail::new("".to_string(), "".to_string());
                 parsing_detail
             }

+ 353 - 0
exchanges/src/socket_tool.rs

@@ -0,0 +1,353 @@
+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;
+use tokio::net::TcpStream;
+use tokio::sync::Mutex;
+use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::trace;
+
+use crate::proxy;
+use crate::proxy::{ProxyEnum, ProxyResponseEnum};
+use crate::response_base::ResponseData;
+
+#[derive(Debug)]
+pub enum HeartbeatType {
+    Ping,
+    Pong,
+}
+
+pub struct AbstractWsMode {}
+
+impl AbstractWsMode {
+    //创建链接
+    pub async fn ws_connect_async<T, PI, PO>(bool_v1: Arc<AtomicBool>,
+                                             address_url: String,
+                                             lable: String,
+                                             subscribe_array: Vec<String>,
+                                             mut write_rx: UnboundedReceiver<Message>,
+                                             read_tx: UnboundedSender<ResponseData>,
+                                             message_text: T,
+                                             message_ping: PI,
+                                             message_pong: PO) -> Result<(), Error>
+        where T: Fn(String) -> Option<ResponseData> + Copy,
+              PI: Fn(Vec<u8>) -> Option<ResponseData> + Copy,
+              PO: Fn(Vec<u8>) -> Option<ResponseData> + Copy
+    {
+        //1.是否走代理
+        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
+        let proxy = match proxy::ParsingDetail::env_proxy(ProxyEnum::WS) {
+            ProxyResponseEnum::NO => {
+                // trace!("非 代理");
+                None
+            }
+            ProxyResponseEnum::YES(proxy) => {
+                // trace!("代理");
+                Option::from(proxy)
+            }
+        };
+
+        loop {
+            let (ws_stream, _) = connect_async(address_url.clone(), proxy).await?;
+            trace!("WebSocket 握手完成。");
+            let (mut write, mut read) = ws_stream.split();
+
+            //订阅写入(包括订阅信息 )
+            trace!("订阅内容:{:?}",subscribe_array.clone());
+            for s in &subscribe_array {
+                write.send(Message::Text(s.parse().unwrap())).await?;
+            }
+
+            //将socket 的写操作与 写通道链接起来,将数据以ok的结构体封装进行传递
+            // let stdin_to_ws = write_rx.map(Ok).forward(write);
+            // Writing task
+            let stdin_to_ws = async {
+                while let Some(message) = write_rx.next().await {
+                    write.send(message).await?;
+                }
+                Ok::<(), tokio_tungstenite::tungstenite::Error>(())
+            };
+            let ws_to_stdout = async {
+                while let Some(message) = read.next().await {
+                    let response_data = AbstractWsMode::analysis_message(message, message_text, message_ping, message_pong);
+                    // let response_data = func(message);
+                    if response_data.is_some() {
+                        let mut data = response_data.unwrap();
+                        data.label = lable.clone();
+                        let code = data.code.clone();
+                        if code.as_str() == "-1" {} else if code.as_str() == "200" {
+                            if bool_v1.load(Ordering::Relaxed) {
+                                read_tx.unbounded_send(data).unwrap();
+                            }
+                        }
+                    }
+                }
+                Ok::<(), tokio_tungstenite::tungstenite::Error>(())
+            };
+            // let ws_to_stdout = {
+            //     trace!("---1");
+            //     //读,循环读取,然后拿到 message,,然后开启异步处理 message,
+            //     let result = read.for_each(|message| async {
+            //         let response_data = func(message);
+            //         if response_data.is_some() {
+            //             let mut data = response_data.unwrap();
+            //             data.label = lable.clone();
+            //             let code = data.code.clone();
+            //             if code.as_str() == "-1" {
+            //                 // let close_frame = CloseFrame {
+            //                 //     code: CloseCode::Normal,
+            //                 //     reason: Cow::Borrowed("Bye bye"),
+            //                 // };
+            //                 // let close_message = Message::Close(Some(close_frame));
+            //                 // write.send(close_message);
+            //             } else if code.as_str() == "200" {
+            //                 read_tx.unbounded_send(data).unwrap();
+            //             }
+            //         }
+            //     });
+            //     trace!("---3");
+            //     result
+            // };
+
+            //必须操作。,因为不同于其他的高级语言,有自动内存管理,所以为了防范地址改变,所以需要做此处理
+            pin_mut!(stdin_to_ws, ws_to_stdout,);
+            future::select(stdin_to_ws, ws_to_stdout).await;
+            trace!("---5");
+            trace!("---4");
+            trace!("重启...");
+        }
+        // return Ok(());
+    }
+    //心跳包
+    pub async fn ping_or_pong(write_tx_clone: Arc<Mutex<UnboundedSender<Message>>>, h_type: HeartbeatType, millis: u64) {
+        loop {
+            tokio::time::sleep(Duration::from_millis(millis)).await;
+            let write_tx_clone = write_tx_clone.lock().await;
+            write_tx_clone.unbounded_send(
+                match h_type {
+                    HeartbeatType::Ping => {
+                        Message::Ping(Vec::from("Ping"))
+                    }
+                    HeartbeatType::Pong => {
+                        Message::Pong(Vec::from("Pong"))
+                    }
+                }
+            ).expect("发送失败");
+            trace!("发送指令-心跳:{:?}",h_type);
+        }
+    }
+    //数据解析
+    pub fn analysis_message<T, PI, PO>(message: Result<Message, Error>,
+                                       message_text: T,
+                                       message_ping: PI,
+                                       message_pong: PO) -> Option<ResponseData>
+        where T: Fn(String) -> Option<ResponseData>,
+              PI: Fn(Vec<u8>) -> Option<ResponseData>,
+              PO: Fn(Vec<u8>) -> Option<ResponseData>
+    {
+        match message {
+            Ok(Message::Text(text)) => message_text(text),
+            Ok(Message::Ping(pi)) => message_ping(pi),
+            Ok(Message::Pong(po)) => message_pong(po),
+            Ok(Message::Binary(s)) => {
+                //二进制WebSocket消息
+                let message_str = format!("Binary:{:?}", s);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(),
+                                               "2".to_string(),
+                                               message_str, "".to_string()))
+            }
+            Ok(Message::Close(c)) => {
+                let message_str = format!("关闭指令:{:?}", c);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(),
+                                               "0".to_string(),
+                                               message_str, "".to_string()))
+            }
+            Ok(Message::Frame(f)) => {
+                //原始帧 正常读取数据不会读取到该 信息类型
+                let message_str = format!("意外读取到原始帧:{:?}", f);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(),
+                                               "-2".to_string(),
+                                               message_str, "".to_string()))
+            }
+            Err(e) => {
+                let message_str = format!("服务器响应:{:?}", e);
+                trace!("{:?}",message_str);
+                Option::from(ResponseData::new("".to_string(),
+                                               "-1".to_string(),
+                                               message_str, "".to_string()))
+            }
+        }
+    }
+    //发送数据
+    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: futures_channel::mpsc::UnboundedSender<Message>) {
+    let _str = serde_json::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
+}

+ 1 - 1
exchanges/src/utils.rs

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

+ 68 - 26
exchanges/tests/binance_spot_test.rs

@@ -1,11 +1,14 @@
-use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+use std::time::Duration;
+use futures_util::StreamExt;
+
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::Message;
 use tracing::trace;
-use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
-use exchanges::response_base::ResponseData;
+
+use exchanges::binance_spot_ws::{BinanceSpotLogin, BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
+use exchanges::socket_tool::AbstractWsMode;
 
 // 账号密码
 const ACCESS_KEY: &str = "";
@@ -17,36 +20,75 @@ const SECRET_KEY: &str = "";
 async fn ws_custom_subscribe() {
     global::log_utils::init_log_with_trace();
 
-    let  bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut  btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, tx);
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let login_param = BinanceSpotLogin {
+        api_key: ACCESS_KEY.to_string(),
+        api_secret: SECRET_KEY.to_string(),
+    };
+    let mut ws = get_ws(None);
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
     ws.set_subscribe(vec![
-        BinanceSpotSubscribeType::PuBookTicker,
-        BinanceSpotSubscribeType::PuAggTrade,
+        // BinanceSpotSubscribeType::PuBookTicker,
+        // BinanceSpotSubscribeType::PuAggTrade,
         BinanceSpotSubscribeType::PuDepth20levels100ms,
-
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["BTCUSDT".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    let bool_v2_clone = Arc::clone(&bool_v1);
+    let write_tx_clone = Arc::clone(&write_tx_am);
+    let su = ws.get_subscription();
+    let tw = tokio::spawn(async move {
+        trace!("线程-数据写入-开始");
+        loop {
+            tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+            // let close_frame = CloseFrame {
+            //     code: CloseCode::Normal,
+            //     reason: Cow::Borrowed("Bye bye"),
+            // };
+            // let message = Message::Close(Some(close_frame));
+
+
+            let message = Message::Text(su.clone());
+            AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+            trace!("发送指令成功");
+        }
+        trace!("线程-数据写入-结束");
+    });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
     });
-    try_join!(t1,t2).unwrap();
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
-fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> BinanceSpotWs {
-    let  binance_ws = BinanceSpotWs::new(false,
-                                            btree_map,
-                                            BinanceSpotWsType::PublicAndPrivate,
-                                            tx);
+fn get_ws(login_param: Option<BinanceSpotLogin>) -> BinanceSpotWs {
+    let binance_ws = BinanceSpotWs::new(false,
+                                        login_param, BinanceSpotWsType::PublicAndPrivate,
+    );
     binance_ws
 }
 

+ 153 - 26
exchanges/tests/binance_swap_test.rs

@@ -1,45 +1,163 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+use std::time::Duration;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::Message;
 use tracing::trace;
+
 use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
-use exchanges::response_base::ResponseData;
+use exchanges::binance_swap_ws::{BinanceSwapLogin, BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
+use exchanges::socket_tool::AbstractWsMode;
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
 
 
 //ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 async fn ws_custom_subscribe() {
     global::log_utils::init_log_with_trace();
 
-    let  bool_v1 = Arc::new(AtomicBool::new(true));
-    let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, tx);
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let mut ws = get_ws(None);
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
     ws.set_subscribe(vec![
-        // BinanceSubscribeType::PuBookTicker,
-        // BinanceSubscribeType::PuAggTrade,
-        BinanceSubscribeType::PuDepth20levels100ms,
+        BinanceSwapSubscribeType::PuBookTicker,
+        // BinanceSwapSubscribeType::PuAggTrade,
+        // BinanceSwapSubscribeType::PuDepth20levels100ms,
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["BTCUSDT".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
+
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
+        // trace!("线程-数据读取-结束");
     });
-    try_join!(t1,t2).unwrap();
-}
+
+    //写数据
+    let bool_v2_clone = Arc::clone(&bool_v1);
+    let write_tx_clone = Arc::clone(&write_tx_am);
+    let su = ws.get_subscription();
+    let tw = tokio::spawn(async move {
+        trace!("线程-数据写入-开始");
+        loop {
+            tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+            // let close_frame = CloseFrame {
+            //     code: CloseCode::Normal,
+            //     reason: Cow::Borrowed("Bye bye"),
+            // };
+            // let message = Message::Close(Some(close_frame));
 
 
+            let message = Message::Text(su.clone());
+            AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+            trace!("发送指令成功");
+        }
+        trace!("线程-数据写入-结束");
+    });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //************************************
+    //11 点31 分
+
+    // let mut bool_v1 = Arc::new(AtomicBool::new(true));
+    // //创建读写通道
+    // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+    // // 封装 write_tx 到 Arc 和 Mutex
+    // let write_tx_am = Arc::new(Mutex::new(write_tx));
+    //
+    // //对象
+    // let mut ws = get_ws(None);
+    // // 币对
+    // ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    // //订阅
+    // ws.set_subscribe(vec![
+    //     BinanceSwapSubscribeType::PuBookTicker,
+    //     BinanceSwapSubscribeType::PuAggTrade,
+    //     BinanceSwapSubscribeType::PuDepth20levels100ms,
+    // ]);
+    //
+    //
+    // //模拟业务场景 开启链接
+    // let bool_v1_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone1 = Arc::clone(&write_tx_am);
+    // let t1 = tokio::spawn(async move {
+    //     ws.ws_connect_async(bool_v1_clone, write_tx_clone1, write_rx, &read_tx).await.unwrap();
+    //     trace!("ws_connect_async 完成");
+    // });
+    //
+    // //模拟业务场景 一直监听数据
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Some(data) = read_rx.next().await {
+    //             trace!("读取数据data:{:?}",data)
+    //         }
+    //     }
+    //     trace!("数据读取退出 完成");
+    // });
+    //
+    //
+    // //模拟用户主动写入数据
+    // // let write_tx_clone2 = Arc::clone(&write_tx_am);
+    // // let t3 = tokio::spawn(async move {
+    // //     //模拟心跳
+    // //     loop {
+    // //         tokio::time::sleep(Duration::from_millis(5000)).await;
+    // //         let mut write_tx_clone = write_tx_clone2.lock().unwrap();
+    // //         match write_tx_clone.unbounded_send(Message::Pong(Vec::from("pong"))) {
+    // //             Ok(_) => {
+    // //                 trace!("发送心跳");
+    // //                 continue;
+    // //             }
+    // //             Err(_) => {
+    // //                 break;
+    // //             }
+    // //         }
+    // //     }
+    // //     trace!("主动推出 完成");
+    // // });
+    // // tokio::try_join!(y,y1,y2).unwrap();
+    // tokio::try_join!(t1,t2).unwrap();
+    // trace!("323123213");
+}
+
 //rest-获取服务器时间
 #[tokio::test]
 async fn rest_get_server_time_test() {
@@ -87,12 +205,21 @@ async fn rest_cancel_order_all_test() {
     trace!("结束时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
 }
 
+//rest-账户成交历史
+#[tokio::test]
+async fn rest_get_user_trades_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_user_trades("BTCUSDT".to_string(), -1, -1, 500).await;
+    trace!(?rep_data)
+}
+
 
-fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> BinanceSwapWs {
-    let  binance_ws = BinanceSwapWs::new(false,
-                                            btree_map,
-                                            BinanceWsType::PublicAndPrivate,
-                                            tx);
+fn get_ws(btree_map: Option<BinanceSwapLogin>) -> BinanceSwapWs {
+    let binance_ws = BinanceSwapWs::new(false,
+                                        btree_map,
+                                        BinanceSwapWsType::PublicAndPrivate);
     binance_ws
 }
 
@@ -101,6 +228,6 @@ fn get_rest() -> BinanceSwapRest {
     btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
     btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
 
-    let  ba_exc = BinanceSwapRest::new(false, btree_map);
+    let ba_exc = BinanceSwapRest::new(false, btree_map);
     ba_exc
 }

+ 135 - 44
exchanges/tests/bitget_spot_test.rs

@@ -1,17 +1,13 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
 use tracing::trace;
-use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
-use exchanges::bitget_spot_rest::{BitgetSpotRest};
-use exchanges::bitget_spot_ws::{BitgetSpotWs, BitgetSubscribeType, BitgetWsType};
-use exchanges::kucoin_swap_rest::KucoinSwapRest;
-use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
-use exchanges::proxy;
-use exchanges::response_base::ResponseData;
+
+use exchanges::bitget_spot_rest::BitgetSpotRest;
+use exchanges::bitget_spot_ws::{BitgetSpotLogin, BitgetSpotSubscribeType, BitgetSpotWs, BitgetSpotWsType};
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
@@ -22,27 +18,67 @@ const PASS_KEY: &str = "";
 async fn ws_custom_subscribe_pu() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, BitgetWsType::Public, tx).await;
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let  bool_v1 = Arc::new(AtomicBool::new(true));
+    let mut ws = get_ws(None, BitgetSpotWsType::Public).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
     ws.set_subscribe(vec![
-        BitgetSubscribeType::PuTicker,
-        BitgetSubscribeType::PuCandle1m,
-        BitgetSubscribeType::PuTrade,
-        BitgetSubscribeType::PuBooks5,
+        BitgetSpotSubscribeType::PuTicker,
+        BitgetSpotSubscribeType::PuCandle1m,
+        BitgetSpotSubscribeType::PuTrade,
+        BitgetSpotSubscribeType::PuBooks5,
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["BTC_USDT".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+//读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
+        // trace!("线程-数据读取-结束");
     });
-    try_join!(t1,t2).unwrap();
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 //ws-订阅私有频道信息
@@ -50,28 +86,72 @@ async fn ws_custom_subscribe_pu() {
 async fn ws_custom_subscribe_pr() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, BitgetWsType::Private, tx).await;
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let login_param = BitgetSpotLogin {
+        api_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        passphrase_key: PASS_KEY.to_string(),
+    };
+    let mut ws = get_ws(None, BitgetSpotWsType::Private).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
     ws.set_subscribe(vec![
-        BitgetSubscribeType::PrAccount,
-        BitgetSubscribeType::PrOrders,
+        BitgetSpotSubscribeType::PuTicker,
+        BitgetSpotSubscribeType::PuCandle1m,
+        BitgetSpotSubscribeType::PuTrade,
+        BitgetSpotSubscribeType::PuBooks5,
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["BTC_USDT".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+//读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
     });
-    try_join!(t1,t2).unwrap();
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 
@@ -404,10 +484,21 @@ async fn rest_wallet_transfer_test() {
     trace!(?rep_data)
 }
 
+//rest-获取账单流水
+#[tokio::test]
+async fn rest_get_account_bills_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_bills("".to_string(),
+                                          "".to_string(),
+                                          "".to_string(),
+                                          "".to_string(), ).await;
+    trace!(?rep_data)
+}
+
 
-async fn get_ws(btree_map: BTreeMap<String, String>, type_v: BitgetWsType, tx: Sender<ResponseData>) -> BitgetSpotWs {
-    let mut ku_ws = BitgetSpotWs::new(false, btree_map.clone(),
-                                      type_v, tx);
+async fn get_ws(btree_map: Option<BitgetSpotLogin>, type_v: BitgetSpotWsType) -> BitgetSpotWs {
+    let mut ku_ws = BitgetSpotWs::new(false, btree_map.clone(), type_v);
     ku_ws
 }
 

+ 74 - 50
exchanges/tests/gate_swap_test.rs

@@ -1,66 +1,91 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
 use tracing::trace;
-use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
+
 use exchanges::gate_swap_rest::GateSwapRest;
-use exchanges::gate_swap_ws::{GateSubscribeType, GateSwapWs, GateWsType};
-use exchanges::proxy;
-use exchanges::response_base::ResponseData;
+use exchanges::gate_swap_ws::{GateSwapLogin, GateSwapSubscribeType, GateSwapWs,  GateSwapWsType};
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
 
-
 //ws-订阅公共频道信息
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn ws_custom_subscribe() {
-    // global::log_utils::init_log_with_trace();
-    //
-    // let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    // let (tx, mut rx) = channel(1024);
-    // let mut ws = get_ws(btree_map, tx);
-    // ws.set_subscribe(vec![
-    //     GateSubscribeType::PuFuturesCandlesticks,
-    //     GateSubscribeType::PuFuturesTrades,
-    //     GateSubscribeType::PuFuturesOrderBook,
-    //
-    //     GateSubscribeType::PrFuturesPositions("".to_string()),
-    // ]);
-    // let t1 = tokio::spawn(async move {
-    //     ws.custom_subscribe(bool_v1, vec!["BTC_USDT".to_string()]).await;
-    // });
-    // let t2 = tokio::spawn(async move {
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             trace!( "age: {:?}", received);
-    //         }
-    //     }
-    // });
-    // try_join!(t1,t2).unwrap();
+    global::log_utils::init_log_with_trace();
 
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
 
+    let param  = GateSwapLogin{
+        api_key: ACCESS_KEY.to_string(),
+        secret: SECRET_KEY.to_string(),
+    };
 
-    let json_str = r#"{"name": "Alice", "age": 30}"#;
+    let mut ws = get_ws(Option::from(param));
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
+    ws.set_subscribe(vec![
+        // GateSwapSubscribeType::PuFuturesOrderBook,
+        // GateSwapSubscribeType::PuFuturesCandlesticks,
+        // GateSwapSubscribeType::PuFuturesTrades,
 
-    // 解析 JSON 字符串并将其转换为一个 Rust 结构体
-    let parsed_data: Result<serde_json::Value, serde_json::Error> = serde_json::from_str(json_str);
+        GateSwapSubscribeType::PrFuturesBalances("".to_string()),
+        // GateSwapSubscribeType::PrFuturesOrders("".to_string()),
+        // GateSwapSubscribeType::PrFuturesPositions("".to_string()),
+    ]);
 
-    match parsed_data {
-        Ok(data) => {
-            println!("Name: {}", data["name"]);
-            println!("Age: {}", data["age"]);
-        }
-        Err(e) => {
-            println!("Failed to parse JSON: {}", e);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
         }
-    }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 
@@ -74,11 +99,10 @@ async fn rest_cancel_order_all_test() {
     println!("okx--设置持仓模式--{:?}", req_data);
 }
 
-fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> GateSwapWs {
-    let mut binance_ws = GateSwapWs::new(false,
+fn get_ws(btree_map: Option<GateSwapLogin>) -> GateSwapWs {
+    let  binance_ws = GateSwapWs::new(false,
                                          btree_map,
-                                         GateWsType::PublicAndPrivate("usdt".to_string()),
-                                         tx);
+                                         GateSwapWsType::PublicAndPrivate("usdt".to_string()));
     binance_ws
 }
 
@@ -87,6 +111,6 @@ fn get_rest() -> GateSwapRest {
     btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
     btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
 
-    let mut ba_exc =  GateSwapRest::new(false, btree_map);
+    let  ba_exc = GateSwapRest::new(false, btree_map);
     ba_exc
 }

+ 133 - 40
exchanges/tests/kucoin_spot_test.rs

@@ -1,42 +1,81 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
 use tracing::trace;
+
 use exchanges::kucoin_spot_rest::KucoinSpotRest;
-use exchanges::kucoin_spot_ws::{KucoinSpotWs, KucoinSubscribeType, KucoinWsType};
-use exchanges::response_base::ResponseData;
+use exchanges::kucoin_spot_ws::{KucoinSpotLogin, KucoinSpotSubscribeType, KucoinSpotWs, KucoinSpotWsType};
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
 const PASS_KEY: &str = "";
 
 //ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 async fn ws_custom_subscribe_pu() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, KucoinWsType::Public, tx).await;
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+
+    let mut ws = get_ws(None, KucoinSpotWsType::Public).await;
+    ws.set_symbols(vec!["BTC-USDT".to_string()]);
     ws.set_subscribe(vec![
-        // KucoinSubscribeType::PuMarketMatch,
-        // KucoinSubscribeType::PuSpotMarketLevel2Depth50,
-        KucoinSubscribeType::PuMarketTicker,
+        KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50,
+        KucoinSpotSubscribeType::PuMarketTicker,
+        KucoinSpotSubscribeType::PuMarketMatch,
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["ACH_USDT".to_string(), "ROSE_USDT".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
     });
-    try_join!(t1,t2).unwrap();
+
+    //写数据
+    let _bool_v2_clone = Arc::clone(&bool_v1);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 //ws-订阅私有频道信息
@@ -44,30 +83,84 @@ async fn ws_custom_subscribe_pu() {
 async fn ws_custom_subscribe_pr() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, KucoinWsType::Private, tx).await;
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let btree_map = KucoinSpotLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
+    let mut ws = get_ws(Option::from(btree_map), KucoinSpotWsType::Public).await;
+    ws.set_symbols(vec!["BTC-USDT".to_string()]);
     ws.set_subscribe(vec![
-        KucoinSubscribeType::PrAccountBalance,
-        KucoinSubscribeType::PrSpotMarketTradeOrders,
-        KucoinSubscribeType::PuMarketTicker,
+        KucoinSpotSubscribeType::PrAccountBalance,
+        KucoinSpotSubscribeType::PrSpotMarketTradeOrders,
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["ACH_USDT".to_string(), "ROSE_USDT".to_string()]).await;
-    });
 
-    let t2 = tokio::spawn(async move {
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
     });
-    try_join!(t1,t2).unwrap();
+
+    //写数据
+    let _bool_v2_clone = Arc::clone(&bool_v1);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
+
+
+//rest-获取成交记录
+#[tokio::test]
+async fn rest_get_fills_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_fills("BTC-USDT".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  -1,
+                                  -1,
+                                  500,
+    ).await;
+    trace!(?rep_data)
 }
 
 
@@ -152,9 +245,9 @@ async fn rest_cancel_order_by_client_id_test() {
 }
 
 
-async fn get_ws(btree_map: BTreeMap<String, String>, type_v: KucoinWsType, tx: Sender<ResponseData>) -> KucoinSpotWs {
-    let mut ku_ws = KucoinSpotWs::new(false, btree_map.clone(),
-                                      type_v, tx).await;
+async fn get_ws(btree_map: Option<KucoinSpotLogin>, type_v: KucoinSpotWsType) -> KucoinSpotWs {
+    let ku_ws = KucoinSpotWs::new(false, btree_map,
+                                  type_v).await;
     ku_ws
 }
 
@@ -166,6 +259,6 @@ fn get_rest() -> KucoinSpotRest {
     btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
     btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
 
-    let mut ku_exc = KucoinSpotRest::new(false, btree_map);
+    let ku_exc = KucoinSpotRest::new(false, btree_map);
     ku_exc
 }

+ 129 - 107
exchanges/tests/kucoin_swap_test.rs

@@ -1,146 +1,168 @@
-use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
 use tracing::trace;
-use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
-use exchanges::kucoin_swap_rest::KucoinSwapRest;
-use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
-use exchanges::proxy;
-use exchanges::response_base::ResponseData;
+
+use exchanges::kucoin_swap_ws::{KucoinSwapLogin, KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
 const PASS_KEY: &str = "";
 
 //ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 async fn ws_custom_subscribe_pu() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, KucoinWsType::Public, tx).await;
-    ws.set_subscribe(vec![
-        KucoinSubscribeType::PuContractMarketExecution,
-        // KucoinSubscribeType::PuContractMarketLevel2Depth50,
-        // KucoinSubscribeType::PuContractMarkettickerV2,
-    ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
-        loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
-            }
-        }
-    });
-    try_join!(t1,t2).unwrap();
-}
 
-//ws-订阅私有频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe_pr() {
-    global::log_utils::init_log_with_trace();
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, KucoinWsType::Private, tx).await;
+    let mut ws = get_ws(None, KucoinSwapWsType::Public).await;
+    ws.set_symbols(vec!["xbt_usdtM".to_string()]);
     ws.set_subscribe(vec![
-        KucoinSubscribeType::PrContractPosition,
-        KucoinSubscribeType::PrContractAccountWallet,
-        KucoinSubscribeType::PrContractMarketTradeOrdersSys,
-        KucoinSubscribeType::PrContractPosition,
+        KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
+        KucoinSwapSubscribeType::PuContractMarketExecution,
+        KucoinSwapSubscribeType::PuContractMarkettickerV2,
     ]);
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
-    });
 
-    let t2 = tokio::spawn(async move {
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
     });
-    try_join!(t1,t2).unwrap();
-}
 
+    //写数据
+    let _bool_v2_clone = Arc::clone(&bool_v1);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+}
 
-//rest-撤销全部订单
-#[tokio::test]
-async fn rest_cancel_orders_test() {
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
+async fn ws_custom_subscribe_pr() {
     global::log_utils::init_log_with_trace();
 
-    let mut rest = get_rest();
-    let rep_data = rest.cancel_orders("BLZUSDTM".to_string()).await;
-    trace!(?rep_data)
-}
 
-//rest-撤销全部订单
-#[tokio::test]
-async fn rest_cancel_order_all_test() {
-    global::log_utils::init_log_with_trace();
+    //对象
+    let btree_map = KucoinSwapLogin {
+        access_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        pass_key: PASS_KEY.to_string(),
+    };
 
-    let mut rest = get_rest();
-    let rep_data = rest.cancel_order_all().await;
-    trace!(?rep_data)
-}
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
 
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
 
-//rest-杠杆测试
-#[tokio::test]
-async fn rest_leverage_test() {
-    global::log_utils::init_log_with_trace();
 
-    // //查询杠杆
-    let mut rest = get_rest();
-    // let rep_data = rest.get_leverage("ADAUSDTM".to_string()).await;
-    // trace!("查询杠杆{:?}",rep_data);
+    let mut ws = get_ws(Option::from(btree_map), KucoinSwapWsType::Private).await;
+    ws.set_symbols(vec!["xbt_usdtM".to_string()]);
+    ws.set_subscribe(vec![
+        // KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
+        // KucoinSwapSubscribeType::PuContractMarketExecution,
+        KucoinSwapSubscribeType::PuContractMarkettickerV2,
+        KucoinSwapSubscribeType::PrContractAccountWallet,
+        KucoinSwapSubscribeType::PrContractPosition,
+        KucoinSwapSubscribeType::PrContractMarketTradeOrdersSys,
+        KucoinSwapSubscribeType::PrContractMarketTradeOrders,
+    ]);
 
-    //设置杠杆
-    let rep_data2 = rest.set_leverage("BLZUSDTM".to_string(), 1).await;
-    trace!("设置杠杆{:?}",rep_data2);
 
-    // //查询杠杆
-    // let rep_data3 = rest.get_leverage("BLZUSDTM".to_string()).await;
-    // trace!("查询杠杆{:?}",rep_data3);
-}
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
 
-//rest-设置自动开启补仓
-#[tokio::test]
-async fn rest_auto_deposit_test() {
-    global::log_utils::init_log_with_trace();
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+    });
 
-    //设置是否自动补仓
-    let mut rest = get_rest();
-    let rep_data = rest.auto_deposit_status("BLZUSDTM".to_string(), false).await;
-    trace!("设置是否自动补仓{:?}",rep_data);
+    //写数据
+    let _bool_v2_clone = Arc::clone(&bool_v1);
+    let _write_tx_clone = Arc::clone(&write_tx_am);
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
+    //         let close_frame = CloseFrame {
+    //             code: CloseCode::Normal,
+    //             reason: Cow::Borrowed("Bye bye"),
+    //         };
+    //         let close_message = Message::Close(Some(close_frame));
+    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    // loop {
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    // }
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 
-async fn get_ws(btree_map: BTreeMap<String, String>, type_v: KucoinWsType, tx: Sender<ResponseData>) -> KucoinSwapWs {
-    let mut ku_ws = KucoinSwapWs::new(false, btree_map.clone(),
-                                      type_v, tx).await;
+async fn get_ws(btree_map: Option<KucoinSwapLogin>, type_v: KucoinSwapWsType) -> KucoinSwapWs {
+    let ku_ws = KucoinSwapWs::new(false, btree_map,
+                                  type_v).await;
     ku_ws
 }
-
-fn get_rest() -> KucoinSwapRest {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-
-    let mut ku_exc = KucoinSwapRest::new(false, btree_map);
-    ku_exc
-}

+ 243 - 53
exchanges/tests/okx_swap_test.rs

@@ -1,18 +1,13 @@
-use std::any::TypeId;
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::try_join;
+
+use futures_util::StreamExt;
+use tokio::sync::Mutex;
 use tracing::trace;
-use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
-use exchanges::kucoin_swap_rest::KucoinSwapRest;
-use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
+
 use exchanges::okx_swap_rest::OkxSwapRest;
-use exchanges::okx_swap_ws::{OkxSubscribeType, OkxSwapWs, OkxWsType};
-use exchanges::proxy;
-use exchanges::response_base::ResponseData;
+use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
@@ -20,80 +15,243 @@ const PASS_KEY: &str = "";
 
 
 //ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 async fn ws_custom_subscribe_pu() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, OkxWsType::Public, tx).await;
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
+    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
+
+
+    let mut ws = get_ws(None, OkxSwapWsType::Public).await;
+    ws.set_symbols(vec!["BTC_USDT".to_string()]);
     ws.set_subscribe(vec![
-        OkxSubscribeType::PuIndexTickers,
-        OkxSubscribeType::PuBooks5,
-        // OkxSubscribeType::Putrades,
-        // OkxSubscribeType::PuBooks50L2tbt,
+        // OkxSwapSubscribeType::PuBooks5,
+        // OkxSwapSubscribeType::Putrades,
+        // OkxSwapSubscribeType::PuBooks50L2tbt,
+        OkxSwapSubscribeType::PuIndexTickers,
     ]);
 
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["BTC-USDT".to_string()]).await;
-    });
 
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
 
-    let t2 = tokio::spawn(async move {
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
+        // trace!("线程-数据读取-结束");
     });
-    try_join!(t1,t2).unwrap();
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 //ws-订阅私有频道信息
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn ws_custom_subscribe_bu() {
     global::log_utils::init_log_with_trace();
+
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
+    let mut ws = get_ws(None, OkxSwapWsType::Business).await;
+    ws.set_symbols(vec!["BTC-USD".to_string()]);
+    ws.set_subscribe(vec![
+        OkxSwapSubscribeType::BuIndexCandle30m,
+    ]);
+
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
+        loop {
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
+            }
+        }
+        // trace!("线程-数据读取-结束");
+    });
+
+    //写数据
+    // let bool_v2_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
 }
 
 //ws-订阅私有频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
 async fn ws_custom_subscribe_pr() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
 
-    let (tx, mut rx) = channel(1024);
-    let mut ws = get_ws(btree_map, OkxWsType::Private, tx).await;
+    let btree_map = OkxSwapLogin {
+        api_key: ACCESS_KEY.to_string(),
+        secret_key: SECRET_KEY.to_string(),
+        passphrase: PASS_KEY.to_string(),
+    };
+    let mut ws = get_ws(Option::from(btree_map), OkxSwapWsType::Private).await;
+    ws.set_symbols(vec!["BTC-USDT".to_string()]);
     ws.set_subscribe(vec![
-        OkxSubscribeType::PrBalanceAndPosition,
-        OkxSubscribeType::PrAccount("USDT".to_string()),
-        // OkxSubscribeType::PrOrders,
-        OkxSubscribeType::PrPositions,
+        OkxSwapSubscribeType::PrAccount("USDT".to_string()),
+        OkxSwapSubscribeType::PrOrders,
+        OkxSwapSubscribeType::PrPositions,
+        OkxSwapSubscribeType::PrBalanceAndPosition,
     ]);
 
-    let t1 = tokio::spawn(async move {
-        ws.custom_subscribe(bool_v1, vec!["BTC-USDT".to_string()]).await;
-    });
 
-    let t2 = tokio::spawn(async move {
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let bool_v1 = Arc::new(AtomicBool::new(true));
+
+    //读取
+    let _bool_v1_clone = Arc::clone(&bool_v1);
+    let _tr = tokio::spawn(async move {
+        trace!("线程-数据读取-开启");
         loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
+            if let Some(data) = read_rx.next().await {
+                trace!("读取数据data:{:?}",data)
             }
         }
+        // trace!("线程-数据读取-结束");
     });
-    try_join!(t1,t2).unwrap();
-}
 
+    //写数据
+    // let bool_v2_clone = Arc::clone(&bool_v1);
+    // let write_tx_clone = Arc::clone(&write_tx_am);
+    // let su = ws.get_subscription();
+    // let tw = tokio::spawn(async move {
+    //     trace!("线程-数据写入-开始");
+    //     loop {
+    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
+    //         // let close_frame = CloseFrame {
+    //         //     code: CloseCode::Normal,
+    //         //     reason: Cow::Borrowed("Bye bye"),
+    //         // };
+    //         // let message = Message::Close(Some(close_frame));
+    //
+    //
+    //         let message = Message::Text(su.clone());
+    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
+    //         trace!("发送指令成功");
+    //     }
+    //     trace!("线程-数据写入-结束");
+    // });
+
+    let t1 = tokio::spawn(async move {
+        //链接
+        let bool_v3_clone = Arc::clone(&bool_v1);
+        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        trace!("test 唯一线程结束--");
+    });
+    tokio::try_join!(t1).unwrap();
+    trace!("当此结束");
+    trace!("重启!");
+    trace!("参考交易所关闭");
+    return;
+    //
+    // let mut bool_v1 = Arc::new(AtomicBool::new(true));
+    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    //
+    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    // btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+    //
+    // let (tx, mut rx) = channel(1024);
+    // let mut ws = get_ws(btree_map, OkxWsType::Private, tx).await;
+    // ws.set_subscribe(vec![
+    //     OkxSubscribeType::PrBalanceAndPosition,
+    //     // OkxSubscribeType::PrAccount("USDT".to_string()),
+    //     OkxSubscribeType::PrOrders,
+    //     OkxSubscribeType::PrPositions,
+    // ]);
+    //
+    // let t1 = tokio::spawn(async move {
+    //     ws.custom_subscribe(bool_v1, vec!["BTC-USDT".to_string()]).await;
+    // });
+    //
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             trace!( "age: {:?}", received);
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
+}
 
 
 //rest-订单查询
@@ -186,6 +344,7 @@ async fn rest_cancel_order_test() {
     let req_data = ret.cancel_order("BTC-USD".to_string(), "1111".to_string(), "".to_string()).await;
     println!("okx--撤单--{:?}", req_data);
 }
+
 //rest-设置杠杆倍数
 #[tokio::test]
 async fn rest_set_leverage_test() {
@@ -195,6 +354,7 @@ async fn rest_set_leverage_test() {
     let req_data = ret.set_leverage("BTC-USDT".to_string(), "5".to_string()).await;
     println!("okx--设置杠杆倍数--{:?}", req_data);
 }
+
 //rest-设置持仓模式
 #[tokio::test]
 async fn rest_set_position_mode_test() {
@@ -205,11 +365,41 @@ async fn rest_set_position_mode_test() {
     println!("okx--设置持仓模式--{:?}", req_data);
 }
 
+//rest-获取历史订单记录(近七天)
+#[tokio::test]
+async fn rest_get_orders_history_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_orders_history("".to_string(),
+                                          "".to_string(),
+                                          "filled".to_string(),
+                                          "".to_string(),
+                                          "".to_string(),
+                                          "".to_string(),
+    ).await;
+    println!("okx--获取历史订单记录--{:?}", req_data);
+}
+
+//rest-获取历史成交数据(近七天)
+#[tokio::test]
+async fn rest_get_trades_test() {
+    global::log_utils::init_log_with_trace();
 
+    let mut ret = get_rest();
+    let req_data = ret.get_trades("".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "100".to_string(),
+    ).await;
+    println!("okx--获取历史成交数据--{:?}", req_data);
+}
 
 
-async fn get_ws(btree_map: BTreeMap<String, String>, type_v: OkxWsType, tx: Sender<ResponseData>) -> OkxSwapWs {
-    let mut ku_ws = OkxSwapWs::new(false, btree_map.clone(), type_v, tx);
+async fn get_ws(btree_map: Option<OkxSwapLogin>, type_v: OkxSwapWsType) -> OkxSwapWs {
+    let ku_ws = OkxSwapWs::new(false, btree_map, type_v);
     ku_ws
 }
 
@@ -219,6 +409,6 @@ fn get_rest() -> OkxSwapRest {
     btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
     btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
 
-    let mut okx_exc = OkxSwapRest::new(false, btree_map.clone());
+    let okx_exc = OkxSwapRest::new(false, btree_map.clone());
     okx_exc
 }

+ 67 - 0
exchanges/tests/socket_tool_test.rs

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

+ 20 - 20
exchanges/tests/test.rs

@@ -482,26 +482,26 @@ async fn demo_rest_ba() {
 }
 
 async fn demo_pub_ws_ba() {
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
-    let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut binance_ws = BinanceSwapWs::new(false, btree_map, BinanceWsType::PublicAndPrivate, tx);
-    binance_ws.set_subscribe(vec![
-        BinanceSubscribeType::PuAggTrade,
-        BinanceSubscribeType::PuDepth20levels100ms,
-        BinanceSubscribeType::PuBookTicker,
-    ]);
-    let t1 = tokio::spawn(async move {
-        binance_ws.custom_subscribe(bool_v1,vec!["BTCUSDT".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
-        loop {
-            if let Ok(received) = rx.try_recv() {
-                trace!( "age: {:?}", received);
-            }
-        }
-    });
-    try_join!(t1,t2).unwrap();
+    // let mut bool_v1 = Arc::new(AtomicBool::new(true));
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut binance_ws = BinanceSwapWs::new(false, btree_map, BinanceWsType::PublicAndPrivate, tx);
+    // binance_ws.set_subscribe(vec![
+    //     BinanceSubscribeType::PuAggTrade,
+    //     BinanceSubscribeType::PuDepth20levels100ms,
+    //     BinanceSubscribeType::PuBookTicker,
+    // ]);
+    // let t1 = tokio::spawn(async move {
+    //     binance_ws.custom_subscribe(bool_v1,vec!["BTCUSDT".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             trace!( "age: {:?}", received);
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
 }
 
 fn demo_get_http_proxy() {

+ 59 - 0
global/src/account_info.rs

@@ -0,0 +1,59 @@
+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,
+}
+
+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(),
+        }
+    }
+}
+
+// 获取文件内容
+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
+}

+ 2 - 5
global/src/export_utils.rs

@@ -2,18 +2,15 @@ use chrono::Local;
 use simple_excel_writer::*;
 use uuid::Uuid;
 
-pub fn export_excel(header_array: Vec<&str>, data: Vec<Vec<&str>>, prefix_name: &str) -> String {
+pub fn export_excel(header_array: Vec<&str>, data: Vec<Vec<String>>, prefix_name: &str) -> String {
     //本地存储路径
     let save_path = "C:/Users/Public/Documents/";
-    //文件代理路径
-    let url_path = "http://127.0.0.1/";
     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 url_name = format!("{}rust_export/{}{}_{}.xlsx", url_path, name, date, uid);
     let mut wb = Workbook::create(&file_name);
     let mut sheet = wb.create_sheet("sheet1");
     // 设置行宽
@@ -37,5 +34,5 @@ pub fn export_excel(header_array: Vec<&str>, data: Vec<Vec<&str>>, prefix_name:
         sw.append_row(Default::default())
     }).expect("写入excel错误!");
     wb.close().expect("关闭excel错误!");
-    url_name
+    file_name
 }

+ 2 - 1
global/src/lib.rs

@@ -2,4 +2,5 @@ pub mod public_params;
 pub mod log_utils;
 pub mod params;
 pub mod trace_stack;
-pub mod export_utils;
+pub mod export_utils;
+pub mod account_info;

+ 1 - 1
global/src/public_params.rs

@@ -10,7 +10,7 @@ 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 = 30*1000;                                // 市场信息延迟限制(单位:毫秒)
+pub const MARKET_DELAY_LIMIT: i64 = 3*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默认值

+ 2 - 0
global/tests/export_utils_test.rs

@@ -3,6 +3,8 @@ use global::export_utils;
 
 #[tokio::test]
 async fn export_excel() {
+    global::log_utils::init_log_with_trace();
+
     let header = vec!["标题1", "标题2"];
     let data = vec![vec!["第一行数据1", "第一行数据2"], vec!["第二行数据1", "第二行数据2"]];
     let file_name = export_utils::export_excel(header,data,"");

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

+ 2 - 0
standard/.gitignore

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

+ 3 - 1
standard/Cargo.toml

@@ -17,4 +17,6 @@ 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"] }
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+toml = "0.5.11"
+futures-channel = "0.3.29"

+ 4 - 2
standard/src/binance_handle.rs

@@ -18,15 +18,17 @@ pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialD
     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["B"].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 };
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t };
     let depth_info = vec![bp, bq, ap, aq];
     SpecialDepth {
         name: label,
         depth: depth_info,
         ticker: ticker_info,
+        t,
     }
 }
 

+ 50 - 0
standard/src/binance_spot_handle.rs

@@ -0,0 +1,50 @@
+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 };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: label,
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+    }
+}
+
+// 处理特殊深度数据
+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;
+}

+ 3 - 1
standard/src/bitget_spot_handle.rs

@@ -120,12 +120,14 @@ pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialD
     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 ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t };
     let depth_info = vec![bp, bq, ap, aq];
     SpecialDepth {
         name: label,
         depth: depth_info,
         ticker: ticker_info,
+        t,
     }
 }

+ 28 - 1
standard/src/gate_handle.rs

@@ -1,10 +1,11 @@
 use std::str::FromStr;
 use rust_decimal::Decimal;
 use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
 use tracing::{debug, error};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
-use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder};
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
 use crate::exchange::ExchangeEnum;
 use crate::handle_info::HandleSwapInfo;
 
@@ -125,6 +126,32 @@ pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
     debug!("format-order-end, gate_handle");
     return rst_order;
 }
+// 处理特殊Ticket信息
+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 depth_asks = format_depth_items(data["asks"].clone());
+    let depth_bids = format_depth_items(data["bids"].clone());
+    let t = Decimal::from_str(&data["t"].to_string()).unwrap();
+
+    let ap = depth_asks[0].price;
+    let bp = depth_bids[0].price;
+    let aq = depth_asks[0].amount;
+    let bq = depth_bids[0].amount;
+    let mp = (bp + ap) * dec!(0.5);
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: label,
+        depth: depth_info,
+        ticker: ticker_info,
+        t,
+    }
+}
 
 // 处理特殊深度数据
 pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {

+ 12 - 3
standard/src/handle_info.rs

@@ -1,4 +1,5 @@
 use std::cmp::Ordering;
+use std::str::FromStr;
 use rust_decimal::{Decimal};
 use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
@@ -48,8 +49,7 @@ impl HandleSwapInfo {
                 binance_handle::handle_special_ticker(res_data)
             }
             ExchangeEnum::GateSwap => {
-                error!("暂未提供此交易所方法!handle_special_ticker:{:?}", exchange);
-                panic!("暂未提供此交易所方法!handle_special_ticker:{:?}", exchange);
+                gate_handle::handle_special_ticker(res_data)
             }
             ExchangeEnum::KucoinSwap => {
                 kucoin_handle::handle_special_ticker(res_data)
@@ -133,34 +133,42 @@ impl HandleSwapInfo {
         let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
         let mut depth_asks: Vec<MarketOrder>;
         let mut depth_bids: Vec<MarketOrder>;
+        let t: Decimal;
         match exchange {
             ExchangeEnum::BinanceSpot => {
                 depth_asks = binance_handle::format_depth_items(res_data_json["asks"].clone());
                 depth_bids = binance_handle::format_depth_items(res_data_json["bids"].clone());
+                t = Decimal::from_str(&res_data_json["lastUpdateId"].to_string()).unwrap();
             }
             ExchangeEnum::BinanceSwap => {
                 depth_asks = binance_handle::format_depth_items(res_data_json["a"].clone());
                 depth_bids = binance_handle::format_depth_items(res_data_json["b"].clone());
+                t = Decimal::from_str(&res_data_json["u"].to_string()).unwrap();
             }
             ExchangeEnum::GateSwap => {
                 depth_asks = gate_handle::format_depth_items(res_data_json["asks"].clone());
                 depth_bids = gate_handle::format_depth_items(res_data_json["bids"].clone());
+                t = Decimal::from_str(&res_data_json["t"].to_string()).unwrap();
             }
             ExchangeEnum::KucoinSwap => {
                 depth_asks = kucoin_handle::format_depth_items(res_data_json["asks"].clone());
                 depth_bids = kucoin_handle::format_depth_items(res_data_json["bids"].clone());
+                t = Decimal::from_str(&res_data_json["sequence"].to_string()).unwrap();
             }
             ExchangeEnum::KucoinSpot => {
                 depth_asks = kucoin_spot_handle::format_depth_items(res_data_json["asks"].clone());
                 depth_bids = kucoin_spot_handle::format_depth_items(res_data_json["bids"].clone());
+                t = Decimal::from_str(&res_data_json["timestamp"].to_string()).unwrap();
             }
             ExchangeEnum::OkxSwap => {
                 depth_asks = okx_handle::format_depth_items(res_data_json[0]["asks"].clone());
                 depth_bids = okx_handle::format_depth_items(res_data_json[0]["bids"].clone());
+                t = Decimal::from_str(&res_data_json[0]["seqId"].to_string()).unwrap();
             }
             ExchangeEnum::BitgetSpot => {
                 depth_asks = bitget_spot_handle::format_depth_items(res_data_json[0]["asks"].clone());
                 depth_bids = bitget_spot_handle::format_depth_items(res_data_json[0]["bids"].clone());
+                t = Decimal::from_str(res_data_json[0]["ts"].as_str().unwrap()).unwrap();
             }
             _ => {
                 error!("未找到该交易所!handle_special_depth: {:?}",exchange);
@@ -219,12 +227,13 @@ impl HandleSwapInfo {
             }
         }
 
-        let ticker_info = SpecialTicker { sell: depth_asks[0].price, buy: depth_bids[0].price, mid_price: mp };
+        let ticker_info = SpecialTicker { sell: depth_asks[0].price, buy: depth_bids[0].price, mid_price: mp, t };
         let depth_info = bp.iter().cloned().chain(bv.iter().cloned()).chain(ap.iter().cloned()).chain(av.iter().cloned()).collect();
         SpecialDepth {
             name: res_data.label,
             depth: depth_info,
             ticker: ticker_info,
+            t,
         }
     }
 }

+ 3 - 1
standard/src/kucoin_handle.rs

@@ -45,13 +45,15 @@ pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialD
     let ap = Decimal::from_str(&data["bestAskPrice"].as_str().unwrap()).unwrap();
     let aq = Decimal::from_f64(data["bestAskSize"].as_f64().unwrap()).unwrap();
     let mp = (bp + ap) * dec!(0.5);
+    let t = Decimal::from_str(&data["sequence"].to_string()).unwrap();
 
-    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t };
     let depth_info = vec![bp, bq, ap, aq];
     SpecialDepth {
         name: label,
         depth: depth_info,
         ticker: ticker_info,
+        t,
     }
 }
 

+ 3 - 1
standard/src/kucoin_spot_handle.rs

@@ -120,12 +120,14 @@ pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialD
     let ap = Decimal::from_str(data["bestAsk"].as_str().unwrap()).unwrap();
     let aq = Decimal::from_str(data["bestAskSize"].as_str().unwrap()).unwrap();
     let mp = (bp + ap) * dec!(0.5);
+    let t = Decimal::from_str(data["sequence"].as_str().unwrap()).unwrap();
 
-    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t };
     let depth_info = vec![bp, bq, ap, aq];
     SpecialDepth {
         name: label,
         depth: depth_info,
         ticker: ticker_info,
+        t,
     }
 }

+ 5 - 0
standard/src/kucoin_swap.rs

@@ -599,6 +599,8 @@ impl Platform for KucoinSwap {
 
             let handle = tokio::spawn(async move {
                 let value = limits_clone[&item_clone].clone();
+                // info!(?value);
+                // info!("{}", ts.to_string());
                 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();
@@ -625,6 +627,9 @@ impl Platform for KucoinSwap {
             });
             handles.push(handle)
         }
+        // if limits.len() > 0 {
+        //     info!("");
+        // }
         // 检查订单指令
         let check = order_command.check;
         for item in check.keys() {

+ 6 - 0
standard/src/lib.rs

@@ -17,6 +17,7 @@ pub mod handle_info;
 mod binance_swap;
 mod binance_spot;
 pub mod binance_handle;
+pub mod binance_spot_handle;
 // 引入gate模块
 mod gate_swap;
 mod gate_spot;
@@ -142,11 +143,13 @@ impl Depth {
 /// - `name<String>`: 平台信息;
 /// - `depth(Vec<Decimal>)`: 深度信息;
 /// - `ticker(SpecialTicker)`: 市场行情;
+/// - ``
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct SpecialDepth {
     pub name: String,
     pub depth: Vec<Decimal>,
     pub ticker: SpecialTicker,
+    pub t: Decimal,
 }
 
 impl SpecialDepth {
@@ -155,6 +158,7 @@ impl SpecialDepth {
             name: "".to_string(),
             depth: vec![],
             ticker: SpecialTicker::new(),
+            t: Default::default(),
         }
     }
 }
@@ -168,6 +172,7 @@ pub struct SpecialTicker {
     pub sell: Decimal,
     pub buy: Decimal,
     pub mid_price: Decimal,
+    pub t: Decimal,
 }
 
 impl SpecialTicker {
@@ -176,6 +181,7 @@ impl SpecialTicker {
             sell: Default::default(),
             buy: Default::default(),
             mid_price: Default::default(),
+            t: Default::default(),
         }
     }
 }

+ 5 - 3
standard/src/okx_handle.rs

@@ -127,13 +127,15 @@ pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialD
     let ap = Decimal::from_str(asks[0].as_str().unwrap()).unwrap();
     let aq = Decimal::from_str(asks[1].as_str().unwrap()).unwrap();
     let mp = (bp + ap) * dec!(0.5);
+    let t = Decimal::from_str(data["ts"].as_str().unwrap()).unwrap();
 
-    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t };
     let depth_info = vec![bp, bq, ap, aq];
     SpecialDepth {
         name: label,
         depth: depth_info,
         ticker: ticker_info,
+        t,
     }
 }
 
@@ -153,12 +155,12 @@ pub fn format_position_item(value: &SwapPosition, ct_val: Decimal) -> Position {
     };
     Position {
         symbol: value.inst_id.replace("-SWAP", ""),
-        margin_level: dec!(-1),
+        margin_level: value.lever,
         amount: value.pos * ct_val,
         frozen_amount: Decimal::ZERO,
         price: value.avg_px,
         profit: value.upl,
         position_mode,
-        margin: value.margin,
+        margin: if value.margin != "" { Decimal::from_str(&value.margin).unwrap() } else { Decimal::ZERO },
     }
 }

+ 330 - 136
standard/src/okx_swap.rs

@@ -9,7 +9,7 @@ use rust_decimal::Decimal;
 use rust_decimal::prelude::FromPrimitive;
 use serde::{Deserialize, Serialize};
 use serde_json::json;
-use tracing::error;
+use tracing::{debug, error};
 use exchanges::okx_swap_rest::OkxSwapRest;
 use global::trace_stack::TraceStack;
 use crate::exchange::ExchangeEnum;
@@ -180,8 +180,8 @@ struct SwapAccountDetails {
 #[derive(Debug, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct SwapPosition {
-    pub adl: Decimal,
-    pub avail_pos: Decimal,
+    pub adl: String,
+    pub avail_pos: String,
     pub avg_px: Decimal,
     pub c_time: String,
     pub ccy: String,
@@ -192,52 +192,51 @@ pub struct SwapPosition {
     pub imr: String,
     pub inst_id: String,
     pub inst_type: String,
-    pub interest: Decimal,
-    pub idx_px: Decimal,
-    pub last: Decimal,
+    pub interest: String,
+    pub idx_px: String,
+    pub last: String,
     pub usd_px: String,
-    pub be_px: Decimal,
+    pub be_px: String,
     pub lever: Decimal,
     pub liab: String,
     pub liab_ccy: String,
-    pub liq_px: Decimal,
-    pub mark_px: Decimal,
-    pub margin: Decimal,
-    pub mgn_mode: Decimal,
-    pub mgn_ratio: Decimal,
-    pub mmr: Decimal,
-    pub notional_usd: Decimal,
+    pub liq_px: String,
+    pub mark_px: String,
+    pub margin: String,
+    pub mgn_mode: String,
+    pub mgn_ratio: String,
+    pub mmr: String,
+    pub notional_usd: String,
     pub opt_val: String,
-    pub p_time: String,
     pub pos: Decimal,
     pub pos_ccy: String,
-    pub pos_id: Decimal,
+    pub pos_id: String,
     pub pos_side: String,
     pub spot_in_use_amt: String,
     pub spot_in_use_ccy: String,
     pub theta_b_s: String,
     pub theta_p_a: String,
-    pub trade_id: Decimal,
+    pub trade_id: String,
     pub biz_ref_id: String,
     pub biz_ref_type: String,
-    pub quote_bal: Decimal,
-    pub base_bal: Decimal,
+    pub quote_bal: String,
+    pub base_bal: String,
     pub base_borrowed: String,
     pub base_interest: String,
     pub quote_borrowed: String,
     pub quote_interest: String,
     pub u_time: String,
     pub upl: Decimal,
-    pub upl_last_px: Decimal,
-    pub upl_ratio: Decimal,
-    pub upl_ratio_last_px: Decimal,
+    pub upl_last_px: String,
+    pub upl_ratio: String,
+    pub upl_ratio_last_px: String,
     pub vega_b_s: String,
     pub vega_p_a: String,
-    pub realized_pnl: Decimal,
-    pub pnl: Decimal,
-    pub fee: Decimal,
-    pub funding_fee: Decimal,
-    pub liq_penalty: Decimal,
+    pub realized_pnl: String,
+    pub pnl: String,
+    pub fee: String,
+    pub funding_fee: String,
+    pub liq_penalty: String,
     pub close_order_algo: Vec<SwapPositionCloseOrderAlgo>,
 }
 
@@ -245,15 +244,15 @@ pub struct SwapPosition {
 #[serde(rename_all = "camelCase")]
 pub struct SwapPositionCloseOrderAlgo {
     pub algo_id: String,
-    pub sl_trigger_px: Decimal,
+    pub sl_trigger_px: String,
     pub sl_trigger_px_type: String,
-    pub tp_trigger_px: Decimal,
+    pub tp_trigger_px: String,
     pub tp_trigger_px_type: String,
-    pub close_fraction: Decimal,
+    pub close_fraction: String,
 }
 
-/// Kucoin交易所行情信息请求数据结构
-/// - 接口`"/api/v1/ticker"`
+/// Okx交易所行情信息请求数据结构
+/// - 接口`"/api/v5/market/ticker"`
 ///
 /// struct SwapTicker
 /// - `inst_type`: String, 产品类型
@@ -293,6 +292,176 @@ struct SwapTicker {
     sod_utc8: Decimal,
 }
 
+/// Okx交易所市场信息请求数据结构
+/// - 接口`"/api/v5/public/instruments"`
+///
+/// struct SwapMarket
+/// - `alias`: String,
+/// - `base_ccy`: String,
+/// - `category`: String,
+/// - `ct_mult`: Decimal,
+/// - `ct_type`: String,
+/// - `ct_val`: Decimal,
+/// - `ct_val_ccy`: String,
+/// - `exp_time`: String,
+/// - `inst_family`: String,
+/// - `inst_id`: String,
+/// - `inst_type`: String,
+/// - `lever`: Decimal,
+/// - `list_time`: String,
+/// - `lot_sz`: Decimal,
+/// - `max_iceberg_sz`: Decimal,
+/// - `max_lmt_sz`: Decimal,
+/// - `max_mkt_sz`: Decimal,
+/// - `max_stop_sz`: Decimal,
+/// - `max_trigger_sz`: Decimal,
+/// - `max_twap_sz`: Decimal,
+/// - `min_sz`: Decimal,
+/// - `opt_type`: String,
+/// - `quote_ccy`: String,
+/// - `settle_ccy`: String,
+/// - `state`: String,
+/// - `stk`: String,
+/// - `tick_sz`: Decimal,
+/// - `uly`: String,
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct SwapMarket {
+    alias: String,
+    base_ccy: String,
+    category: String,
+    ct_mult: Decimal,
+    ct_type: String,
+    ct_val: Decimal,
+    ct_val_ccy: String,
+    exp_time: String,
+    inst_family: String,
+    inst_id: String,
+    inst_type: String,
+    lever: Decimal,
+    list_time: String,
+    lot_sz: Decimal,
+    max_iceberg_sz: Decimal,
+    max_lmt_sz: Decimal,
+    max_mkt_sz: Decimal,
+    max_stop_sz: Decimal,
+    max_trigger_sz: Decimal,
+    max_twap_sz: Decimal,
+    min_sz: Decimal,
+    opt_type: String,
+    quote_ccy: String,
+    settle_ccy: String,
+    state: String,
+    stk: String,
+    tick_sz: Decimal,
+    uly: String,
+}
+
+/// Okx交易所订单信息请求数据结构
+/// - 接口`"/api/v5/trade/order"`
+///
+/// struct SwapOrder
+/// - `inst_type`: String, 产品类型
+/// - `inst_id`: String, 产品ID
+/// - `ccy`: String, 保证金币种
+/// - `ord_id`: String, 订单ID
+/// - `cl_ord_id`: String, 客户自定义订单ID
+/// - `tag`: String, 订单标签
+/// - `px`: Decimal, 委托价格
+/// - `px_usd`: String, 期权价格
+/// - `px_vol`: String, 期权订单的隐含波动率
+/// - `px_type`: String, 期权的价格类型
+/// - `sz`: Decimal, 委托数量
+/// - `pnl`: Decimal, 收益
+/// - `ord_type`: String, 订单类型
+/// - `side`: String, 订单方向
+/// - `pos_side`: String, 持仓方向
+/// - `td_mode`: String, 交易模式
+/// - `acc_fill_sz`: Decimal, 累计成交数量
+/// - `fill_px`: String, 最新成交价格,如果成交数量为0,该字段为""
+/// - `trade_id`: String, 最新成交ID
+/// - `fill_sz`: Decimal, 最新成交数量
+/// - `fill_time`: String, 最新成交时间
+/// - `source`: String, 订单来源
+/// - `state`: String, 订单状态
+/// - `avg_px`: Decimal, 成交均价,如果成交数量为0,该字段也为""
+/// - `lever`: Decimal, 杠杆倍数
+/// - `attach_algo_cl_ord_id`: String, 下单附带止盈止损时,客户自定义的策略订单ID
+/// - `tp_trigger_px`: Decimal, 止盈触发价
+/// - `tp_trigger_px_type`: String, 止盈触发价类型
+/// - `tp_ord_px`: Decimal, 止盈委托价
+/// - `sl_trigger_px`: Decimal, 止损触发价
+/// - `sl_trigger_px_type`: String, 止损触发价类型
+/// - `sl_ord_px`: Decimal, 止损委托价
+/// - `stp_id`: String, 自成交保护ID
+/// - `stp_mode`: String, 自成交保护模式
+/// - `fee_ccy`: String, 交易手续费币种
+/// - `fee`: Decimal, 手续费与返佣
+/// - `rebate_ccy`: String, 返佣金币种
+/// - `rebate`: String, 返佣金额,仅适用于币币和杠杆
+/// - `tgt_ccy`: String, 币币市价单委托数量sz的单位
+/// - `category`: String, 订单种类
+/// - `reduce_only`: String, 是否只减仓
+/// - `cancel_source`: String, 	订单取消来源的原因枚举值代码
+/// - `cancel_source_reason`: String, 订单取消来源的对应具体原因
+/// - `quick_mgn_type`: String, 一键借币类型,仅适用于杠杆逐仓的一键借币模式
+/// - `algo_cl_ord_id`: String, 客户自定义策略订单ID
+/// - `algo_id`: String, 策略委托单ID,策略订单触发时有值,否则为""
+/// - `u_time`: String, 订单状态更新时间
+/// - `c_time`: String, 订单创建时间
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SwapOrder {
+    inst_type: String,
+    inst_id: String,
+    ccy: String,
+    ord_id: String,
+    cl_ord_id: String,
+    tag: String,
+    px: Decimal,
+    px_usd: String,
+    px_vol: String,
+    px_type: String,
+    sz: Decimal,
+    pnl: String,
+    ord_type: String,
+    side: String,
+    pos_side: String,
+    td_mode: String,
+    acc_fill_sz: Decimal,
+    fill_px: String,
+    trade_id: String,
+    fill_sz: String,
+    fill_time: String,
+    source: String,
+    state: String,
+    avg_px: String,
+    lever: String,
+    attach_algo_cl_ord_id: String,
+    tp_trigger_px: String,
+    tp_trigger_px_type: String,
+    tp_ord_px: String,
+    sl_trigger_px: String,
+    sl_trigger_px_type: String,
+    sl_ord_px: String,
+    stp_id: String,
+    stp_mode: String,
+    fee_ccy: String,
+    fee: String,
+    rebate_ccy: String,
+    rebate: String,
+    tgt_ccy: String,
+    category: String,
+    reduce_only: String,
+    cancel_source: String,
+    cancel_source_reason: String,
+    quick_mgn_type: String,
+    algo_cl_ord_id: String,
+    algo_id: String,
+    u_time: String,
+    c_time: String,
+}
+
 #[allow(dead_code)]
 #[derive(Clone)]
 pub struct OkxSwap {
@@ -363,23 +532,24 @@ impl Platform for OkxSwap {
             let res_data_str = &res_data.data;
             let balance_info_list: Vec<SwapAccount> = serde_json::from_str(res_data_str).unwrap();
             let detail = balance_info_list[0].details.iter().find(|&item| item.ccy == symbol_array[1]);
-            let balance_info: SwapAccountDetails = match detail {
+            match detail {
                 None => {
                     error!("Okx:获取Account信息错误!\nhandle_swap_account:res_data={:?}", res_data);
                     panic!("Okx:获取Account信息错误!\nhandle_swap_account:res_data={:?}", res_data)
                 }
-                Some(value) => { value.clone() }
-            };
-            let result = Account {
-                coin: balance_info.ccy.to_uppercase(),
-                balance: balance_info.avail_bal + balance_info.fixed_bal,
-                available_balance: balance_info.avail_bal,
-                frozen_balance: balance_info.fixed_bal,
-                stocks: Decimal::ZERO,
-                available_stocks: Decimal::ZERO,
-                frozen_stocks: Decimal::ZERO,
-            };
-            Ok(result)
+                Some(value) => {
+                    let result = Account {
+                        coin: value.ccy.to_uppercase(),
+                        balance: value.avail_bal + value.fixed_bal,
+                        available_balance: value.avail_bal,
+                        frozen_balance: value.fixed_bal,
+                        stocks: Decimal::ZERO,
+                        available_stocks: Decimal::ZERO,
+                        frozen_stocks: Decimal::ZERO,
+                    };
+                    Ok(result)
+                }
+            }
         } else {
             Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
@@ -390,23 +560,15 @@ impl Platform for OkxSwap {
     }
 
     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
-        let symbol_format = utils::format_symbol(self.symbol.to_string(), "-");
+        let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.to_string(), "-"));
         let ct_val = self.market.ct_val;
         let res_data = self.request.get_positions("SWAP".to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let data_list: Vec<SwapPosition> = serde_json::from_str(&res_data_str).unwrap();
-            let position_info = data_list.iter().find(|&item| item.inst_id == format!("{}-SWAP", symbol_format));
-            match position_info {
-                None => {
-                    error!("okx_swap:获取Position信息错误!\nget_position:res_data={:?}", res_data_str);
-                    panic!("okx_swap:获取Position信息错误!\nget_position:res_data={:?}", res_data_str)
-                }
-                Some(value) => {
-                    let result = okx_handle::format_position_item(value, ct_val);
-                    Ok(vec![result])
-                }
-            }
+            let position_info: Vec<&SwapPosition> = data_list.iter().filter(|item| item.inst_id == symbol_format).collect();
+            let result = position_info.iter().map(|item| okx_handle::format_position_item(item, ct_val)).collect();
+            Ok(result)
         } else {
             Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
@@ -425,12 +587,12 @@ impl Platform for OkxSwap {
     }
 
     async fn get_ticker(&mut self) -> Result<Ticker, Error> {
-        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
         let res_data = self.request.get_ticker(symbol_format.clone()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let ticker_info_list: Vec<SwapTicker> = serde_json::from_str(&res_data_str).unwrap();
-            let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format.clone()));
+            let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == symbol_format);
             match ticker_info {
                 None => {
                     error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
@@ -455,12 +617,12 @@ impl Platform for OkxSwap {
     }
 
     async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
-        let symbol_format = utils::format_symbol(symbol.clone(), "-");
+        let symbol_format = format!("{}-SWAP", utils::format_symbol(symbol.clone(), "-"));
         let res_data = self.request.get_ticker(symbol_format.clone()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let ticker_info_list: Vec<SwapTicker> = serde_json::from_str(&res_data_str).unwrap();
-            let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format.clone()));
+            let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == symbol_format);
             match ticker_info {
                 None => {
                     error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
@@ -489,41 +651,29 @@ impl Platform for OkxSwap {
         let res_data = self.request.get_instruments().await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
-            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let market_info = res_data_json.iter().find(|item| item["instId"].as_str().unwrap() == format!("{}-SWAP", symbol_format));
+            let res_data_json: Vec<SwapMarket> = serde_json::from_str(res_data_str).unwrap();
+            let market_info = res_data_json.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format));
             match market_info {
                 None => {
                     error!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
                     panic!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
                 }
                 Some(value) => {
-                    let base_asset = value["ctValCcy"].as_str().unwrap_or("").to_string();
-                    let quote_asset = value["settleCcy"].as_str().unwrap_or("").to_string();
-                    let tick_size = Decimal::from_str(&value["tickSz"].as_str().unwrap()).unwrap();
-                    let min_qty = Decimal::from_str(&value["minSz"].as_str().unwrap()).unwrap();
-                    let max_qty = Decimal::from_str(&value["maxLmtSz"].as_str().unwrap()).unwrap();
-                    let ct_val = Decimal::from_str(&value["ctVal"].as_str().unwrap()).unwrap();
-
-
-                    let amount_size = min_qty * ct_val;
-                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
-                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
-                    let min_notional = min_qty * ct_val;
-                    let max_notional = max_qty * ct_val;
-
+                    println!("{:?}", value);
+                    let symbol_array: Vec<&str> = value.inst_family.split("-").collect();
                     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,
-                        ct_val,
+                        symbol: format!("{}_{}", symbol_array[0], symbol_array[1]),
+                        base_asset: symbol_array[0].to_string(),
+                        quote_asset: symbol_array[1].to_string(),
+                        tick_size: value.tick_sz,
+                        amount_size: value.min_sz * value.ct_val,
+                        price_precision: Decimal::from_u32(value.tick_sz.scale()).unwrap(),
+                        amount_precision: Decimal::from_u32((value.min_sz * value.ct_val).scale()).unwrap(),
+                        min_qty: value.min_sz,
+                        max_qty: value.max_lmt_sz,
+                        min_notional: value.min_sz * value.ct_val,
+                        max_notional: value.max_lmt_sz * value.ct_val,
+                        ct_val: value.ct_val,
                     };
                     Ok(result)
                 }
@@ -538,41 +688,28 @@ impl Platform for OkxSwap {
         let res_data = self.request.get_instruments().await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
-            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let market_info = res_data_json.iter().find(|item| item["instId"].as_str().unwrap() == format!("{}-SWAP", symbol_format));
+            let res_data_json: Vec<SwapMarket> = serde_json::from_str(res_data_str).unwrap();
+            let market_info = res_data_json.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format));
             match market_info {
                 None => {
                     error!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
                     panic!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
                 }
                 Some(value) => {
-                    let base_asset = value["ctValCcy"].as_str().unwrap_or("").to_string();
-                    let quote_asset = value["settleCcy"].as_str().unwrap_or("").to_string();
-                    let tick_size = Decimal::from_str(&value["tickSz"].as_str().unwrap()).unwrap();
-                    let min_qty = Decimal::from_str(&value["minSz"].as_str().unwrap()).unwrap();
-                    let max_qty = Decimal::from_str(&value["maxLmtSz"].as_str().unwrap()).unwrap();
-                    let ct_val = Decimal::from_str(&value["ctVal"].as_str().unwrap()).unwrap();
-
-
-                    let amount_size = min_qty * ct_val;
-                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
-                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
-                    let min_notional = min_qty * ct_val;
-                    let max_notional = max_qty * ct_val;
-
+                    let symbol_array: Vec<&str> = value.inst_family.split("-").collect();
                     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,
-                        ct_val,
+                        symbol: format!("{}_{}", symbol_array[0], symbol_array[1]),
+                        base_asset: symbol_array[0].to_string(),
+                        quote_asset: symbol_array[1].to_string(),
+                        tick_size: value.tick_sz,
+                        amount_size: value.min_sz * value.ct_val,
+                        price_precision: Decimal::from_u32(value.tick_sz.scale()).unwrap(),
+                        amount_precision: Decimal::from_u32((value.min_sz * value.ct_val).scale()).unwrap(),
+                        min_qty: value.min_sz,
+                        max_qty: value.max_lmt_sz,
+                        min_notional: value.min_sz * value.ct_val,
+                        max_notional: value.max_lmt_sz * value.ct_val,
+                        ct_val: value.ct_mult,
                     };
                     Ok(result)
                 }
@@ -583,13 +720,14 @@ impl Platform for OkxSwap {
     }
 
     async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
         let ct_val = self.market.ct_val;
-        let res_data = self.request.get_order(self.symbol.clone(), order_id.to_string(), custom_id.to_string()).await;
+        let res_data = self.request.get_order(symbol_format, order_id.to_string(), custom_id.to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
-            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let res_data_json: Vec<SwapOrder> = serde_json::from_str(res_data_str).unwrap();
             let order_info = res_data_json[0].clone();
-            let result = okx_handle::format_order_item(order_info, ct_val);
+            let result = format_order_item(order_info, ct_val);
             Ok(result)
         } else {
             Err(Error::new(ErrorKind::Other, res_data.to_string()))
@@ -597,13 +735,13 @@ impl Platform for OkxSwap {
     }
 
     async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
-        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
         let ct_val = self.market.ct_val;
         let res_data = self.request.get_incomplete_order(symbol_format.clone()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
-            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let result = res_data_json.iter().map(|item| okx_handle::format_order_item(item.clone(), ct_val)).collect();
+            let res_data_json: Vec<SwapOrder> = serde_json::from_str(res_data_str).unwrap();
+            let result = res_data_json.iter().map(|item| format_order_item(item.clone(), ct_val)).collect();
             Ok(result)
         } else {
             Err(Error::new(ErrorKind::Other, res_data.to_string()))
@@ -611,7 +749,7 @@ impl Platform for OkxSwap {
     }
 
     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 symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
         let ct_val = self.market.ct_val;
         let mut params = json!({
             "tdMode": "cross",
@@ -625,23 +763,40 @@ impl Platform for OkxSwap {
         match origin_side {
             "kd" => {
                 params["side"] = json!("buy");
+                params["posSide"] = json!("long");
             }
             "pd" => {
                 params["side"] = json!("sell");
+                params["posSide"] = json!("long");
             }
             "kk" => {
                 params["side"] = json!("sell");
+                params["posSide"] = json!("short");
             }
             "pk" => {
                 params["side"] = json!("buy");
+                params["posSide"] = json!("short");
             }
             _ => { error!("下单参数错误"); }
         };
         let res_data = self.request.swap_order(params).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
-            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
-            let result = okx_handle::format_order_item(res_data_json, ct_val);
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let order_info = res_data_json[0].clone();
+            let id = order_info["ordId"].as_str().unwrap().to_string();
+            let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id,
+                price,
+                amount,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("799 okx_swap".to_string()),
+            };
             Ok(result)
         } else {
             Err(Error::new(ErrorKind::Other, res_data.to_string()))
@@ -649,7 +804,7 @@ impl Platform for OkxSwap {
     }
 
     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 symbol_format = format!("{}-SWAP", utils::format_symbol(symbol.clone(), "-"));
         let mut params = json!({
             "tdMode": "cross",
             "clOrdId": custom_id.to_string(),
@@ -662,23 +817,40 @@ impl Platform for OkxSwap {
         match origin_side {
             "kd" => {
                 params["side"] = json!("buy");
+                params["posSide"] = json!("long");
             }
             "pd" => {
                 params["side"] = json!("sell");
+                params["posSide"] = json!("long");
             }
             "kk" => {
                 params["side"] = json!("sell");
+                params["posSide"] = json!("short");
             }
             "pk" => {
                 params["side"] = json!("buy");
+                params["posSide"] = json!("short");
             }
             _ => { error!("下单参数错误"); }
         };
         let res_data = self.request.swap_order(params).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
-            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
-            let result = okx_handle::format_order_item(res_data_json, ct_val);
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let order_info = res_data_json[0].clone();
+            let id = order_info["ordId"].as_str().unwrap().to_string();
+            let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id,
+                price,
+                amount,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("853 okx_swap".to_string()),
+            };
             Ok(result)
         } else {
             Err(Error::new(ErrorKind::Other, res_data.to_string()))
@@ -686,12 +858,8 @@ impl Platform for OkxSwap {
     }
 
     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 symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
         let res_data = self.request.cancel_order(symbol_format, order_id.to_string(), custom_id.to_string()).await;
-        if order_id == "" {
-            error!("okx_swap:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id);
-            panic!("okx_swap:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id)
-        }
         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();
@@ -707,7 +875,7 @@ impl Platform for OkxSwap {
                 avg_price: Decimal::ZERO,
                 status: "REMOVE".to_string(),
                 order_type: "".to_string(),
-                trace_stack: TraceStack::default().on_special("428 okx_handle".to_string()),
+                trace_stack: TraceStack::default().on_special("879 okx_swap".to_string()),
             };
             Ok(result)
         } else {
@@ -735,7 +903,7 @@ impl Platform for OkxSwap {
     }
 
     async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
-        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
         let res_data = self.request.set_leverage(symbol_format, leverage.to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
@@ -857,3 +1025,29 @@ impl Platform for OkxSwap {
     }
 }
 
+pub fn format_order_item(data: SwapOrder, ct_val: Decimal) -> Order {
+    debug!("format-order-start, okx_swap");
+    debug!(?data);
+    let custom_status = if ["canceled", "filled", "mmp_canceled"].contains(&data.state.as_str()) {
+        "REMOVE".to_string()
+    } else if ["live", "partially_filled"].contains(&data.state.as_str()) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+
+    let result = Order {
+        id: data.ord_id,
+        custom_id: data.cl_ord_id,
+        price: data.px,
+        amount: data.sz * ct_val,
+        deal_amount: data.acc_fill_sz * ct_val,
+        avg_price: if data.avg_px != "" { Decimal::from_str(&data.avg_px).unwrap() } else { Decimal::ZERO },
+        status: custom_status,
+        order_type: data.ord_type,
+        trace_stack: TraceStack::default().on_special("1049 okx_swap".to_string()),
+    };
+    debug!(?result);
+    debug!("format-order-end, okx_swap");
+    result
+}

+ 31 - 0
standard/tests/binance_handle_test.rs

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

+ 1 - 1
standard/tests/binance_spot_handle_test.rs

@@ -4,7 +4,7 @@ use exchanges::binance_spot_ws::BinanceSpotSubscribeType;
 use standard::exchange::ExchangeEnum;
 use crate::exchange_test::test_new_exchange_wss;
 
-const SYMBOL: &str = "BLZ_USDT";
+const SYMBOL: &str = "BTC_USDT";
 
 // 测试订阅深度信息
 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]

+ 5 - 5
standard/tests/bitget_spot_handle_test.rs

@@ -1,7 +1,7 @@
 mod exchange_test;
 
 use tracing::{instrument};
-use exchanges::bitget_spot_ws::{BitgetSubscribeType};
+use exchanges::bitget_spot_ws::{BitgetSpotSubscribeType};
 use standard::exchange::ExchangeEnum;
 use crate::exchange_test::test_new_exchange_wss;
 
@@ -14,7 +14,7 @@ async fn test_get_wss_depth() {
     global::log_utils::init_log_with_trace();
 
     let bitget_subscribe_type = vec![
-        BitgetSubscribeType::PuBooks5
+        BitgetSpotSubscribeType::PuBooks5
     ];
     test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_subscribe_type, "depth").await;
 }
@@ -26,7 +26,7 @@ async fn test_get_wss_ticker() {
     global::log_utils::init_log_with_trace();
 
     let bitget_subscribe_type = vec![
-        BitgetSubscribeType::PuTicker
+        BitgetSpotSubscribeType::PuTicker
     ];
     test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_subscribe_type, "ticker").await;
 }
@@ -38,7 +38,7 @@ async fn test_get_wss_account() {
     global::log_utils::init_log_with_trace();
 
     let bitget_subscribe_type = vec![
-        BitgetSubscribeType::PrAccount
+        BitgetSpotSubscribeType::PrAccount
     ];
     test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_subscribe_type, "account").await;
 }
@@ -50,7 +50,7 @@ async fn test_get_wss_orders() {
     global::log_utils::init_log_with_trace();
 
     let bitget_subscribe_type = vec![
-        BitgetSubscribeType::PrOrders
+        BitgetSpotSubscribeType::PrOrders
     ];
     test_new_exchange_wss(ExchangeEnum::BitgetSpot, SYMBOL, bitget_subscribe_type, "orders").await;
 }

+ 347 - 218
standard/tests/exchange_test.rs

@@ -1,25 +1,28 @@
 use std::collections::{BTreeMap};
-use std::{env};
-use std::io::Error;
+use std::io::{Error};
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::time::Duration;
+use futures::StreamExt;
 use rust_decimal_macros::dec;
 use tokio::sync::mpsc::{channel, Receiver, Sender};
+use tokio::sync::Mutex;
 use tokio::try_join;
 use tracing::{error, trace};
-// use exchanges::bitget_spot_ws::{BitgetSpotWs, BitgetSubscribeType, BitgetWsType};
-use exchanges::kucoin_spot_ws::{KucoinSpotWs, KucoinSubscribeType, KucoinWsType};
-// use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
-// use exchanges::okx_swap_ws::{OkxSubscribeType, OkxSwapWs, OkxWsType};
-// use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
+// use exchanges::binance_spot_ws::{BinanceSpotLogin, BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
+// use exchanges::binance_swap_ws::{BinanceSwapLogin, BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
+// use exchanges::kucoin_swap_ws::{KucoinSwapLogin, KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
+// use exchanges::kucoin_spot_ws::{KucoinSpotLogin, KucoinSpotSubscribeType, KucoinSpotWs, KucoinSpotWsType};
+// use exchanges::gate_swap_ws::{GateSwapLogin, GateSwapSubscribeType, GateSwapWs, GateSwapWsType};
+// use exchanges::bitget_spot_ws::{BitgetSpotLogin, BitgetSpotSubscribeType, BitgetSpotWs, BitgetSpotWsType};
+use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
 use exchanges::response_base::ResponseData;
 use standard::exchange::{Exchange, ExchangeEnum};
+// use standard::{binance_spot_handle, Order, Platform, utils};
 // use standard::{binance_handle, Order, Platform, utils};
-// use standard::{okx_handle, Order, Platform, utils};
 // use standard::{kucoin_handle, Order, Platform, utils};
-use standard::{kucoin_spot_handle, Order, Platform, utils};
+// use standard::{gate_handle, Order, Platform, utils};
 // use standard::{bitget_spot_handle, Order, Platform, utils};
+use standard::{okx_handle, Order, Platform, utils};
 
 // 创建实体
 #[allow(dead_code)]
@@ -27,44 +30,46 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
     utils::proxy_handle();
     let (order_sender, _order_receiver): (Sender<Order>, Receiver<Order>) = channel(1024);
     let (error_sender, _error_receiver): (Sender<Error>, Receiver<Error>) = channel(1024);
+
+    let account_info = global::account_info::get_account_info("../test_account.toml");
     match exchange {
         ExchangeEnum::BinanceSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("binance_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("binance_secret_key").unwrap_or("".to_string());
+            let access_key = account_info.binance_access_key;
+            let secret_key = account_info.binance_secret_key;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
         ExchangeEnum::BinanceSpot => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("binance_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("binance_secret_key").unwrap_or("".to_string());
+            let access_key = account_info.binance_access_key;
+            let secret_key = account_info.binance_secret_key;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
         ExchangeEnum::GateSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("gate_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("gate_secret_key").unwrap_or("".to_string());
+            let access_key = account_info.gate_access_key;
+            let secret_key = account_info.gate_secret_key;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
         ExchangeEnum::GateSpot => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("gate_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("gate_secret_key").unwrap_or("".to_string());
+            let access_key = account_info.gate_access_key;
+            let secret_key = account_info.gate_secret_key;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
         ExchangeEnum::KucoinSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("kucoin_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("kucoin_secret_key").unwrap_or("".to_string());
-            let pass_key = env::var("kucoin_pass_key").unwrap_or("".to_string());
+            let access_key = account_info.kucoin_access_key;
+            let secret_key = account_info.kucoin_secret_key;
+            let pass_key = account_info.kucoin_pass;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             params.insert("pass_key".to_string(), pass_key);
@@ -72,9 +77,9 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
         }
         ExchangeEnum::KucoinSpot => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("kucoin_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("kucoin_secret_key").unwrap_or("".to_string());
-            let pass_key = env::var("kucoin_pass_key").unwrap_or("".to_string());
+            let access_key = account_info.kucoin_access_key;
+            let secret_key = account_info.kucoin_secret_key;
+            let pass_key = account_info.kucoin_pass;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             params.insert("pass_key".to_string(), pass_key);
@@ -82,9 +87,9 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
         }
         ExchangeEnum::OkxSwap => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("okx_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("okx_secret_key").unwrap_or("".to_string());
-            let pass_key = env::var("okx_passphrase").unwrap_or("".to_string());
+            let access_key = account_info.okx_access_key;
+            let secret_key = account_info.okx_secret_key;
+            let pass_key = account_info.okx_pass;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             params.insert("pass_key".to_string(), pass_key);
@@ -92,9 +97,9 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
         }
         ExchangeEnum::BitgetSpot => {
             let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("bitget_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("bitget_secret_key").unwrap_or("".to_string());
-            let pass_key = env::var("bitget_pass_key").unwrap_or("".to_string());
+            let access_key = account_info.bitget_access_key;
+            let secret_key = account_info.bitget_secret_key;
+            let pass_key = account_info.bitget_pass;
             params.insert("access_key".to_string(), access_key);
             params.insert("secret_key".to_string(), secret_key);
             params.insert("pass_key".to_string(), pass_key);
@@ -104,45 +109,46 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
 }
 
 #[allow(dead_code)]
-pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<KucoinSubscribeType>: From<T> {
+pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<OkxSwapSubscribeType>: From<T> {
     utils::proxy_handle();
-
+    let account_info = global::account_info::get_account_info("../test_account.toml");
     match exchange {
         ExchangeEnum::BinanceSpot => {
             // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
             // trace!(symbol_format);
-            // let name = format!("binance_usdt_swap@{}", symbol.to_string().to_lowercase());
+            // let name = format!("binance_spot@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
             // let bool_v1 = Arc::new(AtomicBool::new(true));
             //
-            // let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
-            // let mut params: BTreeMap<String, String> = BTreeMap::new();
-            // let access_key = env::var("binance_access_key").unwrap_or("".to_string());
-            // let secret_key = env::var("binance_secret_key").unwrap_or("".to_string());
-            // params.insert("access_key".to_string(), access_key);
-            // params.insert("secret_key".to_string(), secret_key);
+            // let params = BinanceSpotLogin {
+            //     api_key: account_info.binance_access_key,
+            //     api_secret: account_info.binance_secret_key,
+            // };
             // let mut exchange_wss;
-            // exchange_wss = BinanceSpotWs::new_label(name, false, params, BinanceSpotWsType::PublicAndPrivate, res_sender);
+            // exchange_wss = BinanceSpotWs::new_label(name, false, Option::from(params), BinanceSpotWsType::PublicAndPrivate);
+            // exchange_wss.set_symbols(vec![symbol_format]);
             // exchange_wss.set_subscribe(subscriber_type.into());
             //
-            // let t1 = tokio::spawn(async move {
-            //     exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
-            // });
+            //
             // let mold_arc = Arc::new(mold.to_string());
-            // let t2 = tokio::spawn(async move {
+            // //读取
+            // tokio::spawn(async move {
             //     let mold_clone = Arc::clone(&mold_arc);
             //     loop {
-            //         tokio::time::sleep(Duration::from_millis(1)).await;
-            //         if let Ok(received) = res_receiver.try_recv() {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
             //             match mold_clone.as_str() {
             //                 "depth" => {
-            //                     if received.data != "" {
-            //                         let result = binance_handle::handle_special_depth(received);
+            //                     if data.data != "" {
+            //                         let result = binance_spot_handle::handle_special_depth(data);
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "ticker" => {
-            //                     if received.data != "" {
-            //                         let result = binance_handle::handle_special_ticker(received);
+            //                     if data.data != "" {
+            //                         let result = binance_spot_handle::handle_special_ticker(data);
             //                         trace!(?result)
             //                     }
             //                 }
@@ -152,73 +158,122 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             //                 }
             //             }
             //         }
-            //     }
+            //     };
             // });
-            // try_join!(t1, t2).unwrap();
-            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
-            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
+            //
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&bool_v1);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
         }
         ExchangeEnum::BinanceSwap => {
-            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
-            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
-        }
-        ExchangeEnum::GateSwap => {
-            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
-            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
+            // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
+            // trace!(symbol_format);
+            // let name = format!("binance_swap@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
+            // let bool_v1 = Arc::new(AtomicBool::new(true));
+            //
+            // let api_key = env::var("binance_access_key").unwrap_or("".to_string());
+            // let api_secret = env::var("binance_secret_key").unwrap_or("".to_string());
+            // let params = BinanceSwapLogin {
+            //     api_key: account_info.binance_access_key,
+            //     api_secret: account_info.binance_secret_key,
+            // };
+            // let mut exchange_wss;
+            // exchange_wss = BinanceSwapWs::new_label(name, false, Option::from(params), BinanceSwapWsType::PublicAndPrivate);
+            // exchange_wss.set_symbols(vec![symbol_format]);
+            // exchange_wss.set_subscribe(subscriber_type.into());
+            //
+            //
+            // let mold_arc = Arc::new(mold.to_string());
+            // //读取
+            // tokio::spawn(async move {
+            //     let mold_clone = Arc::clone(&mold_arc);
+            //     loop {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if data.data != "" {
+            //                         let result = binance_handle::handle_special_depth(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if data.data != "" {
+            //                         let result = binance_handle::handle_special_ticker(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     };
+            // });
+            //
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&bool_v1);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
         }
         ExchangeEnum::KucoinSwap => {
             // let symbol_format = format!("{}M", utils::format_symbol(symbol.to_string(), ""));
             // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
-            // let name = format!("kucoin_usdt_swap@{}", symbol.to_string().to_lowercase());
+            //
+            // let name = format!("kucoin_swap@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
             // let bool_v1 = Arc::new(AtomicBool::new(true));
             //
-            // let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
-            // let mut params: BTreeMap<String, String> = BTreeMap::new();
-            // let access_key = env::var("kucoin_access_key").unwrap_or("".to_string());
-            // let secret_key = env::var("kucoin_secret_key").unwrap_or("".to_string());
-            // let pass_key = env::var("kucoin_pass_key").unwrap_or("".to_string());
-            // params.insert("access_key".to_string(), access_key);
-            // params.insert("secret_key".to_string(), secret_key);
-            // params.insert("pass_key".to_string(), pass_key);
+            // let params = KucoinSwapLogin {
+            //     access_key: account_info.kucoin_access_key,
+            //     secret_key: account_info.kucoin_secret_key,
+            //     pass_key: account_info.kucoin_pass,
+            // };
             // let mut exchange_wss;
             // if ["depth", "ticker"].contains(&mold) {
-            //     exchange_wss = KucoinSwapWs::new_label(name, false, params, KucoinWsType::Public, res_sender).await
+            //     exchange_wss = KucoinSwapWs::new_label(name, false, Option::from(params), KucoinSwapWsType::Public).await;
             // } else {
-            //     exchange_wss = KucoinSwapWs::new_label(name, false, params, KucoinWsType::Private, res_sender).await
+            //     exchange_wss = KucoinSwapWs::new_label(name, false, Option::from(params), KucoinSwapWsType::Private).await;
             // }
+            // exchange_wss.set_symbols(vec![symbol_format]);
             // exchange_wss.set_subscribe(subscriber_type.into());
             //
-            // let t1 = tokio::spawn(async move {
-            //     exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
-            // });
             // let mold_arc = Arc::new(mold.to_string());
-            // let t2 = tokio::spawn(async move {
+            // tokio::spawn(async move {
             //     let mold_clone = Arc::clone(&mold_arc);
             //     loop {
-            //         tokio::time::sleep(Duration::from_millis(1)).await;
-            //         if let Ok(received) = res_receiver.try_recv() {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
             //             match mold_clone.as_str() {
             //                 "depth" => {
-            //                     let result = kucoin_handle::handle_special_depth(received);
+            //                     let result = kucoin_handle::handle_special_depth(data);
             //                     trace!(?result)
             //                 }
             //                 "ticker" => {
-            //                     let result = kucoin_handle::handle_special_ticker(received);
+            //                     let result = kucoin_handle::handle_special_ticker(data);
             //                     trace!(?result)
             //                 }
             //                 "account" => {
-            //                     trace!(?received);
-            //                     let result = kucoin_handle::handle_account_info(received, symbol_back.clone());
+            //                     let result = kucoin_handle::handle_account_info(data, symbol_back.clone());
             //                     trace!(?result)
             //                 }
             //                 "position" => {
-            //                     trace!(?received);
-            //                     let result = kucoin_handle::handle_position(received, dec!(1));
+            //                     let result = kucoin_handle::handle_position(data, dec!(1));
             //                     trace!(?result)
             //                 }
             //                 "orders" => {
-            //                     trace!(?received);
-            //                     let result = kucoin_handle::handle_order(received, dec!(0.001));
+            //                     let result = kucoin_handle::handle_order(data, dec!(0.001));
             //                     trace!(?result)
             //                 }
             //                 _ => {
@@ -229,132 +284,128 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             //         }
             //     }
             // });
-            // try_join!(t1, t2).unwrap();
+            //
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&bool_v1);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
         }
         ExchangeEnum::KucoinSpot => {
-            let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
-            let symbol_back = utils::format_symbol(symbol.to_string(), "_");
-            trace!(symbol_format);
-            let name = format!("kucoin_usdt_spot@{}", symbol.to_string().to_lowercase());
-            let bool_v1 = Arc::new(AtomicBool::new(true));
-
-            let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
-            let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("kucoin_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("kucoin_secret_key").unwrap_or("".to_string());
-            let pass_key = env::var("kucoin_pass_key").unwrap_or("".to_string());
-            params.insert("access_key".to_string(), access_key);
-            params.insert("secret_key".to_string(), secret_key);
-            params.insert("pass_key".to_string(), pass_key);
-
-            let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
-                KucoinSpotWs::new_label(name, false, params, KucoinWsType::Public, res_sender).await
-            } else {
-                KucoinSpotWs::new_label(name, false, params, KucoinWsType::Private, res_sender).await
-            };
-
-            exchange_wss.set_subscribe(subscriber_type.into());
-
-            let t1 = tokio::spawn(async move {
-                exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
-            });
-            let mold_arc = Arc::new(mold.to_string());
-            let t2 = tokio::spawn(async move {
-                let mold_clone = Arc::clone(&mold_arc);
-                loop {
-                    tokio::time::sleep(Duration::from_millis(1)).await;
-                    if let Ok(received) = res_receiver.try_recv() {
-                        trace!(?received);
-                        match mold_clone.as_str() {
-                            "depth" => {
-                                if received.data != "" {
-                                    let result = kucoin_spot_handle::handle_special_depth(received);
-                                    trace!(?result)
-                                }
-                            }
-                            "ticker" => {
-                                if received.data != "" {
-                                    let result = kucoin_spot_handle::handle_special_ticker(received);
-                                    trace!(?result)
-                                }
-                            }
-                            "account" => {
-                                if received.data != "" {
-                                    let result = kucoin_spot_handle::handle_account_info(received, symbol_back.clone());
-                                    trace!(?result)
-                                }
-                            }
-                            "orders" => {
-                                if received.data != "" {
-                                    let result = kucoin_spot_handle::handle_order(received, dec!(1));
-                                    trace!(?result)
-                                }
-                            }
-                            _ => {
-                                error!("没有该命令!mode={}", mold_clone);
-                                panic!("没有该命令!mode={}", mold_clone)
-                            }
-                        }
-                    }
-                }
-            });
-            try_join!(t1, t2).unwrap();
-        }
-        ExchangeEnum::BitgetSpot => {
             // let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
             // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
             // trace!(symbol_format);
-            // let name = format!("bitget_usdt_spot@{}", symbol.to_string().to_lowercase());
+            // let name = format!("kucoin_spot@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
             // let bool_v1 = Arc::new(AtomicBool::new(true));
             //
-            // let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
-            // let mut params: BTreeMap<String, String> = BTreeMap::new();
-            // let access_key = env::var("bitget_access_key").unwrap_or("".to_string());
-            // let secret_key = env::var("bitget_secret_key").unwrap_or("".to_string());
-            // let pass_key = env::var("bitget_pass_key").unwrap_or("".to_string());
-            // params.insert("access_key".to_string(), access_key);
-            // params.insert("secret_key".to_string(), secret_key);
-            // params.insert("pass_key".to_string(), pass_key);
-            //
+            // let params = KucoinSpotLogin {
+            //     access_key: account_info.kucoin_access_key,
+            //     secret_key: account_info.kucoin_secret_key,
+            //     pass_key: account_info.kucoin_pass,
+            // };
             // let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
-            //     BitgetSpotWs::new_label(name, false, params, BitgetWsType::Public, res_sender)
+            //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Public).await
             // } else {
-            //     BitgetSpotWs::new_label(name, false, params, BitgetWsType::Private, res_sender)
+            //     KucoinSpotWs::new_label(name, false, Option::from(params), KucoinSpotWsType::Private).await
             // };
-            //
+            // exchange_wss.set_symbols(vec![symbol_format]);
             // exchange_wss.set_subscribe(subscriber_type.into());
             //
+            // let mold_arc = Arc::new(mold.to_string());
+            // tokio::spawn(async move {
+            //     let mold_clone = Arc::clone(&mold_arc);
+            //     loop {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_special_depth(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_special_ticker(data);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "account" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_account_info(data, symbol_back.clone());
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "orders" => {
+            //                     if data.data != "" {
+            //                         let result = kucoin_spot_handle::handle_order(data, dec!(1));
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     }
+            // });
             // let t1 = tokio::spawn(async move {
-            //     exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&bool_v1);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
             // });
+            // try_join!(t1).unwrap();
+        }
+        ExchangeEnum::GateSwap => {
+            // let symbol_format = utils::format_symbol(symbol.to_string(), "_").to_uppercase();
+            // trace!(symbol_format);
+            // let name = format!("gate_swap@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
+            // let bool_v1 = Arc::new(AtomicBool::new(true));
+            //
+            // let params = GateSwapLogin {
+            //     api_key: account_info.gate_access_key,
+            //     secret: account_info.gate_secret_key,
+            // };
+            // let mut exchange_wss = GateSwapWs::new_label(name, false, Option::from(params), GateSwapWsType::PublicAndPrivate("usdt".to_string()));
+            // exchange_wss.set_symbols(vec![symbol_format.clone()]);
+            // exchange_wss.set_subscribe(subscriber_type.into());
+            //
             // let mold_arc = Arc::new(mold.to_string());
-            // let t2 = tokio::spawn(async move {
+            // tokio::spawn(async move {
             //     let mold_clone = Arc::clone(&mold_arc);
             //     loop {
-            //         tokio::time::sleep(Duration::from_millis(1)).await;
-            //         if let Ok(received) = res_receiver.try_recv() {
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
             //             match mold_clone.as_str() {
             //                 "depth" => {
-            //                     if received.data != "" {
-            //                         let result = bitget_spot_handle::handle_special_depth(received);
+            //                     if data.data != "" {
+            //                         let result = gate_handle::handle_special_depth(data);
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "ticker" => {
-            //                     if received.data != "" {
-            //                         let result = bitget_spot_handle::handle_special_ticker(received);
+            //                     if data.data != "" {
+            //                         let result = gate_handle::handle_special_ticker(data);
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "account" => {
-            //                     if received.data != "" {
-            //                         let result = bitget_spot_handle::handle_account_info(received, symbol_back.clone());
+            //                     if data.data != "" {
+            //                         let result = gate_handle::handle_account_info(data, symbol_format.clone());
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "orders" => {
-            //                     if received.data != "" {
-            //                         let result = bitget_spot_handle::handle_order(received, dec!(1));
+            //                     if data.data != "" {
+            //                         let result = gate_handle::handle_order(data, dec!(1));
             //                         trace!(?result)
             //                     }
             //                 }
@@ -366,71 +417,66 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             //         }
             //     }
             // });
-            // try_join!(t1, t2).unwrap();
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&bool_v1);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
         }
-        ExchangeEnum::OkxSwap => {
+        ExchangeEnum::BitgetSpot => {
             // let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
             // let symbol_back = utils::format_symbol(symbol.to_string(), "_");
             // trace!(symbol_format);
-            // let name = format!("okx_usdt_swap@{}", symbol.to_string().to_lowercase());
+            // let name = format!("bitget_spot@{}", symbol.to_string().to_lowercase());
+            // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            // let write_tx_am = Arc::new(Mutex::new(write_tx));
             // let bool_v1 = Arc::new(AtomicBool::new(true));
             //
-            // let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
-            // let mut params: BTreeMap<String, String> = BTreeMap::new();
-            // let access_key = env::var("okx_access_key").unwrap_or("".to_string());
-            // let secret_key = env::var("okx_secret_key").unwrap_or("".to_string());
-            // let passphrase = env::var("okx_passphrase").unwrap_or("".to_string());
-            // params.insert("access_key".to_string(), access_key);
-            // params.insert("secret_key".to_string(), secret_key);
-            // params.insert("pass_key".to_string(), passphrase);
+            // let params = BitgetSpotLogin {
+            //     api_key: account_info.bitget_access_key,
+            //     secret_key: account_info.bitget_secret_key,
+            //     passphrase_key: account_info.bitget_pass,
+            // };
             //
             // let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
-            //     OkxSwapWs::new_label(name, false, params, OkxWsType::Public, res_sender)
-            // } else if ["account", "orders", "position"].contains(&mold) {
-            //     OkxSwapWs::new_label(name, false, params, OkxWsType::Private, res_sender)
+            //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Public)
             // } else {
-            //     OkxSwapWs::new_label(name, false, params, OkxWsType::Business, res_sender)
+            //     BitgetSpotWs::new_label(name, false, Option::from(params), BitgetSpotWsType::Private)
             // };
-            //
+            // exchange_wss.set_symbols(vec![symbol_format]);
             // exchange_wss.set_subscribe(subscriber_type.into());
             //
-            // let t1 = tokio::spawn(async move {
-            //     exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
-            // });
             // let mold_arc = Arc::new(mold.to_string());
-            // let t2 = tokio::spawn(async move {
-            //     let mold_clone = Arc::clone(&mold_arc);
+            // //读取
+            // tokio::spawn(async move {
             //     loop {
-            //         tokio::time::sleep(Duration::from_millis(1)).await;
-            //         if let Ok(received) = res_receiver.try_recv() {
+            //         let mold_clone = Arc::clone(&mold_arc);
+            //         if let Some(data) = read_rx.next().await {
+            //             trace!("原始数据 data:{:?}",data);
             //             match mold_clone.as_str() {
             //                 "depth" => {
-            //                     if received.data != "" {
-            //                         let result = okx_handle::handle_special_depth(received);
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_special_depth(data);
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "ticker" => {
-            //                     if received.data != "" {
-            //                         let result = okx_handle::handle_special_ticker(received);
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_special_ticker(data);
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "account" => {
-            //                     if received.data != "" {
-            //                         let result = okx_handle::handle_account_info(received, symbol_back.clone());
-            //                         trace!(?result)
-            //                     }
-            //                 }
-            //                 "position" => {
-            //                     if received.data != "" {
-            //                         let result = okx_handle::handle_position(received, dec!(10));
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_account_info(data, symbol_back.clone());
             //                         trace!(?result)
             //                     }
             //                 }
             //                 "orders" => {
-            //                     if received.data != "" {
-            //                         let result = okx_handle::handle_order(received, dec!(10));
+            //                     if data.data != "" {
+            //                         let result = bitget_spot_handle::handle_order(data, dec!(1));
             //                         trace!(?result)
             //                     }
             //                 }
@@ -442,7 +488,90 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             //         }
             //     }
             // });
-            // try_join!(t1, t2).unwrap();
+            // let t1 = tokio::spawn(async move {
+            //     //链接
+            //     let bool_v3_clone = Arc::clone(&bool_v1);
+            //     exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            // });
+            // try_join!(t1).unwrap();
+        }
+        ExchangeEnum::OkxSwap => {
+            let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+            trace!(symbol_format);
+            let name = format!("okx_swap@{}", symbol.to_string().to_lowercase());
+            let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+            let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
+            let write_tx_am = Arc::new(Mutex::new(write_tx));
+            let bool_v1 = Arc::new(AtomicBool::new(true));
+
+            let params = OkxSwapLogin {
+                api_key: account_info.okx_access_key,
+                secret_key: account_info.okx_secret_key,
+                passphrase: account_info.okx_pass,
+            };
+
+            let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
+                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Public)
+            } else if ["account", "orders", "position"].contains(&mold) {
+                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Private)
+            } else {
+                OkxSwapWs::new_label(name, false, Option::from(params), OkxSwapWsType::Business)
+            };
+
+            exchange_wss.set_symbols(vec![symbol_format.clone()]);
+            exchange_wss.set_subscribe(subscriber_type.into());
+
+            let mold_arc = Arc::new(mold.to_string());
+            tokio::spawn(async move {
+                let mold_clone = Arc::clone(&mold_arc);
+                loop {
+                    if let Some(data) = read_rx.next().await {
+                        match mold_clone.as_str() {
+                            "depth" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_special_depth(data);
+                                    trace!(?result)
+                                }
+                            }
+                            "ticker" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_special_ticker(data);
+                                    trace!(?result)
+                                }
+                            }
+                            "account" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_account_info(data, symbol_format.clone());
+                                    trace!(?result)
+                                }
+                            }
+                            "position" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_position(data, dec!(10));
+                                    trace!(?result)
+                                }
+                            }
+                            "orders" => {
+                                if data.data != "" {
+                                    let result = okx_handle::handle_order(data, dec!(10));
+                                    trace!(?result)
+                                }
+                            }
+                            _ => {
+                                error!("没有该命令!mode={}", mold_clone);
+                                panic!("没有该命令!mode={}", mold_clone)
+                            }
+                        }
+                    }
+                }
+            });
+
+            let t1 = tokio::spawn(async move {
+                //链接
+                let bool_v3_clone = Arc::clone(&bool_v1);
+                exchange_wss.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+            });
+            try_join!(t1).unwrap();
         }
         _ => {
             error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);

+ 83 - 11
standard/tests/gate_handle_test.rs

@@ -1,20 +1,92 @@
 mod exchange_test;
 
-use tracing::{instrument};
-use exchanges::gate_swap_ws::GateSubscribeType;
+use std::collections::BTreeMap;
+use tracing::{instrument, trace};
+use exchanges::gate_swap_rest::GateSwapRest;
+use exchanges::gate_swap_ws::GateSwapSubscribeType;
 use standard::exchange::ExchangeEnum;
-use crate::exchange_test::test_new_exchange_wss;
+use crate::exchange_test::{test_new_exchange_wss};
 
-const SYMBOL: &str = "BLZ_USDT";
+const SYMBOL: &str = "BTC_USDT";
 
-// 测试获取Exchange实体
-#[tokio::test]
+async fn get_user_id() -> String {
+    let account_info = global::account_info::get_account_info("../test_account.toml");
+    let mut params: BTreeMap<String, String> = BTreeMap::new();
+    let access_key = account_info.gate_access_key;
+    let secret_key = account_info.gate_secret_key;
+    params.insert("access_key".to_string(), access_key);
+    params.insert("secret_key".to_string(), secret_key);
+
+    let mut gate_request = GateSwapRest::new(false, params);
+    let res_data = gate_request.wallet_fee().await;
+    if res_data.code == "200" {
+        let res_data_json: serde_json::Value = serde_json::from_str(&res_data.data).unwrap();
+        res_data_json["user_id"].to_string()
+    } else {
+        "".to_string()
+    }
+}
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PuFuturesOrderBook
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "depth").await;
+}
+
+// 测试订阅Ticker信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PuFuturesOrderBook
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "ticker").await;
+}
+
+// 测试订阅Account信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_account() {
+    global::log_utils::init_log_with_trace();
+
+    let user_id = get_user_id().await;
+    trace!(?user_id);
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PrFuturesBalances(user_id)
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "account").await;
+}
+
+// 测试订阅Position信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_position() {
+    global::log_utils::init_log_with_trace();
+
+    let user_id = get_user_id().await;
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PrFuturesPositions(user_id)
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "position").await;
+}
+
+// 测试订阅Orders信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
 #[instrument(level = "TRACE")]
-async fn test_get_self_exchange() {
+async fn test_get_wss_orders() {
     global::log_utils::init_log_with_trace();
 
-    // let gate_subscribe_type = vec![
-    //     GateSubscribeType::PuFuturesOrderBook
-    // ];
-    // test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, gate_subscribe_type, "depth").await;
+    let user_id = get_user_id().await;
+    let gate_subscribe_type = vec![
+        GateSwapSubscribeType::PrFuturesOrders(user_id)
+    ];
+    test_new_exchange_wss(ExchangeEnum::GateSwap, SYMBOL, gate_subscribe_type, "orders").await;
 }

+ 6 - 6
standard/tests/kucoin_handle_test.rs

@@ -1,7 +1,7 @@
 mod exchange_test;
 
 use tracing::{instrument};
-use exchanges::kucoin_swap_ws::{KucoinSubscribeType};
+use exchanges::kucoin_swap_ws::{KucoinSwapSubscribeType};
 use standard::exchange::ExchangeEnum;
 use crate::exchange_test::test_new_exchange_wss;
 
@@ -14,7 +14,7 @@ async fn test_get_wss_depth() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PuContractMarketLevel2Depth50
+        KucoinSwapSubscribeType::PuContractMarketLevel2Depth50
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_subscribe_type, "depth").await;
 }
@@ -26,7 +26,7 @@ async fn test_get_wss_ticker() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PuContractMarkettickerV2
+        KucoinSwapSubscribeType::PuContractMarkettickerV2
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_subscribe_type, "ticker").await;
 }
@@ -38,7 +38,7 @@ async fn test_get_wss_account() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PrContractAccountWallet
+        KucoinSwapSubscribeType::PrContractAccountWallet
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_subscribe_type, "account").await;
 }
@@ -50,7 +50,7 @@ async fn test_get_wss_position() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PrContractPosition
+        KucoinSwapSubscribeType::PrContractPosition
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_subscribe_type, "position").await;
 }
@@ -62,7 +62,7 @@ async fn test_get_wss_orders() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PrContractMarketTradeOrders
+        KucoinSwapSubscribeType::PrContractMarketTradeOrders
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSwap, SYMBOL, kucoin_subscribe_type, "orders").await;
 }

+ 5 - 5
standard/tests/kucoin_spot_handle_test.rs

@@ -1,7 +1,7 @@
 mod exchange_test;
 
 use tracing::{instrument};
-use exchanges::kucoin_spot_ws::{KucoinSubscribeType};
+use exchanges::kucoin_spot_ws::{KucoinSpotSubscribeType};
 use standard::exchange::ExchangeEnum;
 use crate::exchange_test::test_new_exchange_wss;
 
@@ -14,7 +14,7 @@ async fn test_get_wss_depth() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PuSpotMarketLevel2Depth50
+        KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_subscribe_type, "depth").await;
 }
@@ -26,7 +26,7 @@ async fn test_get_wss_ticker() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PuMarketTicker
+        KucoinSpotSubscribeType::PuMarketTicker
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_subscribe_type, "ticker").await;
 }
@@ -38,7 +38,7 @@ async fn test_get_wss_account() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PrAccountBalance
+        KucoinSpotSubscribeType::PrAccountBalance
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_subscribe_type, "account").await;
 }
@@ -50,7 +50,7 @@ async fn test_get_wss_orders() {
     global::log_utils::init_log_with_trace();
 
     let kucoin_subscribe_type = vec![
-        KucoinSubscribeType::PrSpotMarketTradeOrders
+        KucoinSpotSubscribeType::PrSpotMarketTradeOrders
     ];
     test_new_exchange_wss(ExchangeEnum::KucoinSpot, SYMBOL, kucoin_subscribe_type, "orders").await;
 }

+ 7 - 7
standard/tests/okx_handle_test.rs

@@ -1,11 +1,11 @@
 mod exchange_test;
 
 use tracing::{instrument};
-use exchanges::okx_swap_ws::OkxSubscribeType;
+use exchanges::okx_swap_ws::OkxSwapSubscribeType;
 use standard::exchange::ExchangeEnum;
 use crate::exchange_test::test_new_exchange_wss;
 
-const SYMBOL: &str = "ARB_USDT";
+const SYMBOL: &str = "BTC_USDT";
 
 // 测试订阅深度订阅
 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
@@ -14,7 +14,7 @@ async fn test_get_wss_depth() {
     global::log_utils::init_log_with_trace();
 
     let okx_subscribe_type = vec![
-        OkxSubscribeType::PuBooks50L2tbt
+        OkxSwapSubscribeType::PuBooks5
     ];
     test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "depth").await;
 }
@@ -26,7 +26,7 @@ async fn test_get_wss_ticker() {
     global::log_utils::init_log_with_trace();
 
     let okx_subscribe_type = vec![
-        OkxSubscribeType::PuBooks5
+        OkxSwapSubscribeType::PuBooks5
     ];
     test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "ticker").await;
 }
@@ -38,7 +38,7 @@ async fn test_get_wss_account() {
     global::log_utils::init_log_with_trace();
 
     let okx_subscribe_type = vec![
-        OkxSubscribeType::PrAccount("USDT".to_string())
+        OkxSwapSubscribeType::PrAccount("USDT".to_string())
     ];
     test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "account").await;
 }
@@ -50,7 +50,7 @@ async fn test_get_wss_position() {
     global::log_utils::init_log_with_trace();
 
     let okx_subscribe_type = vec![
-        OkxSubscribeType::PrPositions
+        OkxSwapSubscribeType::PrPositions
     ];
     test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "position").await;
 }
@@ -62,7 +62,7 @@ async fn test_get_wss_orders() {
     global::log_utils::init_log_with_trace();
 
     let okx_subscribe_type = vec![
-        OkxSubscribeType::PrOrders
+        OkxSwapSubscribeType::PrOrders
     ];
     test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "orders").await;
 }

+ 36 - 2
standard/tests/okx_swap_test.rs

@@ -1,3 +1,4 @@
+use rust_decimal_macros::dec;
 use tracing::{instrument, trace};
 use standard::exchange::ExchangeEnum;
 use standard::Platform;
@@ -5,7 +6,7 @@ use crate::exchange_test::test_new_exchange;
 
 mod exchange_test;
 
-const SYMBOL: &str = "ETH_USDT";
+const SYMBOL: &str = "CRO_USDT";
 
 // 测试获取Exchange实体
 #[tokio::test]
@@ -157,7 +158,7 @@ async fn test_get_order_detail() {
     global::log_utils::init_log_with_trace();
 
     let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
-    let okx_get_order_detail = okx_swap_exchange.get_order_detail("", "999999").await;
+    let okx_get_order_detail = okx_swap_exchange.get_order_detail("", "999997").await;
     trace!(?okx_get_order_detail);
 }
 
@@ -172,6 +173,39 @@ async fn test_get_orders_list() {
     trace!(?okx_get_orders_list);
 }
 
+// 测试下单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_take_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_take_order = okx_swap_exchange.take_order("999997", "kk", dec!(0.0901), dec!(100)).await;
+    trace!(?okx_take_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_order() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_cancel_order = okx_swap_exchange.cancel_order("", "999998").await;
+    trace!(?okx_cancel_order);
+}
+
+// 测试撤销订单
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_cancel_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_cancel_orders = kucoin_swap_exchange.cancel_orders().await;
+    trace!(?kucoin_cancel_orders);
+}
+
 // 测试设置持仓模式
 #[tokio::test]
 #[instrument(level = "TRACE")]

+ 5 - 1
strategy/Cargo.toml

@@ -19,4 +19,8 @@ tracing-subscriber = "0.3.17"
 standard = { path = "../standard" }
 global = { path = "../global" }
 exchanges = { path = "../exchanges" }
-ndarray = "0.15.6"
+ndarray = "0.15.6"
+
+futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] }
+futures-channel = "0.3.28"
+tokio-tungstenite= { git = "https://github.com/HonestHouLiang/tokio-tungstenite.git",rev = "208fc9b09bcc2e2c8cb52e1cde5087446464fc91"  }

+ 46 - 50
strategy/src/binance_spot.rs

@@ -1,55 +1,51 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::time::Duration;
 use rust_decimal::Decimal;
 use serde_json::Value;
-use tokio::spawn;
-use tokio::sync::mpsc::channel;
 use tokio::sync::Mutex;
-use tokio::time::sleep;
-use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
 use standard::exchange::ExchangeEnum::BinanceSpot;
-use standard::SpecialTicker;
 use crate::model::{OriginalTicker, OriginalTradeBa};
 use crate::quant::Quant;
 
 // 参考 币安 现货 启动
-pub async fn reference_binance_spot_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mutex<Quant>>, name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>){
-    let (tx, mut rx) = channel(100);
-    spawn(async move {
-        let mut ba_exc = BinanceSpotWs::new_label(name, false, exchange_params, BinanceSpotWsType::PublicAndPrivate, tx);
-        ba_exc.set_subscribe(vec![
-            BinanceSpotSubscribeType::PuAggTrade,
-            BinanceSpotSubscribeType::PuBookTicker,
-            BinanceSpotSubscribeType::PuDepth20levels100ms
-        ]);
-        ba_exc.custom_subscribe(bool_v1, symbols.clone()).await;
-    });
-
-    spawn(async move {
-        let bot_arc_clone = Arc::clone(&quant_arc);
-        // trade
-        let mut max_buy = Decimal::ZERO;
-        let mut min_sell = Decimal::ZERO;
-        // ticker
-        let mut update_flag_u = 0i64;
-        loop {
-            sleep(Duration::from_millis(1)).await;
-
-            match rx.try_recv() {
-                Ok(data) => {
-                    on_data(bot_arc_clone.clone(), &mut update_flag_u, &mut max_buy, &mut min_sell, data).await;
-                },
-                Err(_e) => { }
-            }
-
-        }
-    });
+#[allow(dead_code)]
+pub async fn reference_binance_spot_run(_bool_v1 :Arc<AtomicBool>, _quant_arc: Arc<Mutex<Quant>>, _name: String, _symbols: Vec<String>, _exchange_params: BTreeMap<String, String>){
+    // let (tx, mut rx) = channel(100);
+    // spawn(async move {
+    //     let mut ba_exc = BinanceSpotWs::new_label(name, false, None, BinanceSpotWsType::PublicAndPrivate);
+    //     ba_exc.set_subscribe(vec![
+    //         // BinanceSpotSubscribeType::PuAggTrade,
+    //         // BinanceSpotSubscribeType::PuBookTicker,
+    //         BinanceSpotSubscribeType::PuDepth20levels100ms
+    //     ]);
+    //     ba_exc.custom_subscribe(bool_v1, symbols.clone()).await;
+    // });
+    //
+    // spawn(async move {
+    //     let bot_arc_clone = Arc::clone(&quant_arc);
+    //     // trade
+    //     let mut max_buy = Decimal::ZERO;
+    //     let mut min_sell = Decimal::ZERO;
+    //     // ticker
+    //     let mut update_flag_u = 0i64;
+    //     loop {
+    //         // sleep(Duration::from_micros(10)).await;
+    //
+    //         match rx.try_recv() {
+    //             Ok(data) => {
+    //                 on_data(bot_arc_clone.clone(), &mut update_flag_u, &mut max_buy, &mut min_sell, data).await;
+    //             },
+    //             Err(_e) => { }
+    //         }
+    //
+    //     }
+    // });
 }
 
+#[allow(dead_code)]
 async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, update_flag_u: &mut i64, max_buy: &mut Decimal, min_sell: &mut Decimal, data: ResponseData) {
     let mut trace_stack = TraceStack::default();
     trace_stack.on_network(data.time);
@@ -80,19 +76,19 @@ async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, update_flag_u: &mut i64, max_
         trace_stack.on_before_format();
         let ticker: OriginalTicker = serde_json::from_str(data.data.as_str()).unwrap();
         if ticker.u > *update_flag_u {
-            {
-                let mut quant = bot_arc_clone.lock().await;
-                // time_delay.quant_start = Utc::now().timestamp_micros();
-                quant._update_ticker(SpecialTicker{
-                    sell: ticker.a.clone(),
-                    buy: ticker.b.clone(),
-                    mid_price: Default::default(),
-                }, data.label.clone());
-                let depth = vec![ticker.b, ticker.B, ticker.a, ticker.A];
-                trace_stack.on_after_format();
-                quant._update_depth(depth.clone(), data.label.clone(), trace_stack.clone());
-                quant.local_depths.insert(data.label.clone(), depth);
-            }
+            // {
+            //     let mut quant = bot_arc_clone.lock().await;
+            //     // time_delay.quant_start = Utc::now().timestamp_micros();
+            //     quant._update_ticker(SpecialTicker{
+            //         sell: ticker.a.clone(),
+            //         buy: ticker.b.clone(),
+            //         mid_price: Default::default(),
+            //     }, data.label.clone());
+            //     let depth = vec![ticker.b, ticker.B, ticker.a, ticker.A];
+            //     trace_stack.on_after_format();
+            //     quant._update_depth(depth.clone(), data.label.clone(), trace_stack.clone());
+            //     quant.local_depths.insert(data.label.clone(), depth);
+            // }
         } else {
             *update_flag_u = ticker.u;
         }

+ 48 - 64
strategy/src/binance_usdt_swap.rs

@@ -1,53 +1,59 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::time::Duration;
 use rust_decimal::Decimal;
-use serde_json::Value;
-use tokio::spawn;
-use tokio::sync::mpsc::channel;
 use tokio::sync::Mutex;
-use tokio::time::sleep;
-use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
 use standard::exchange::ExchangeEnum::BinanceSwap;
-use standard::SpecialTicker;
-use crate::model::{OriginalTicker, OriginalTradeBa};
+use crate::model::{OriginalTradeBa};
 use crate::quant::Quant;
+use exchanges::binance_swap_ws::{BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
+use futures_util::StreamExt;
+use crate::exchange_disguise::on_special_depth;
 
 // 参考 币安 合约 启动
-pub(crate) async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mutex<Quant>>, name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>){
-    let (tx, mut rx) = channel(100);
-    spawn( async move {
-        let mut ba_exc = BinanceSwapWs::new_label(name, false, exchange_params, BinanceWsType::PublicAndPrivate, tx);
-        ba_exc.set_subscribe(vec![
-            BinanceSubscribeType::PuBookTicker,
-            BinanceSubscribeType::PuAggTrade
+pub(crate) async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mutex<Quant>>,
+                                               name: String, symbols: Vec<String>, _exchange_params: BTreeMap<String, String>) {
+    tokio::spawn(async move {
+        //创建读写通道
+        let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+        let mut ws = BinanceSwapWs::new_label(name, false, None, BinanceSwapWsType::PublicAndPrivate);
+        ws.set_symbols(symbols);
+        ws.set_subscribe(vec![
+            // BinanceSwapSubscribeType::PuDepth20levels100ms,
+            BinanceSwapSubscribeType::PuBookTicker,
+            // BinanceSwapSubscribeType::PuAggTrade
         ]);
-        ba_exc.custom_subscribe(bool_v1, symbols.clone()).await;
-    });
-    let bot_arc_clone = Arc::clone(&quant_arc);
-    spawn(async move {
-        // ticker
-        let mut update_flag_u = 0i64;
-        // trade
-        let mut max_buy = Decimal::ZERO;
-        let mut min_sell = Decimal::ZERO;
-        loop {
-            sleep(Duration::from_millis(1)).await;
 
-            match rx.try_recv() {
-                Ok(data) => {
+        //读取数据
+        let bot_arc_clone = Arc::clone(&quant_arc);
+        tokio::spawn(async move {
+            // ticker
+            let mut update_flag_u = Decimal::ZERO;
+            // trade
+            let mut max_buy = Decimal::ZERO;
+            let mut min_sell = Decimal::ZERO;
+
+            loop {
+                if let Some(data) = read_rx.next().await {
                     on_data(bot_arc_clone.clone(), &mut update_flag_u, &mut max_buy, &mut min_sell, data).await;
-                },
-                Err(_e) => { }
+                }
             }
-        }
+        });
+
+        // 链接
+        let write_tx_am = Arc::new(Mutex::new(write_tx));
+        ws.ws_connect_async(bool_v1, &write_tx_am, write_rx, read_tx).await.expect("链接失败");
     });
 }
 
-async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, update_flag_u: &mut i64, max_buy: &mut Decimal, min_sell: &mut Decimal, data: ResponseData) {
+async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>,
+                 update_flag_u: &mut Decimal,
+                 max_buy: &mut Decimal,
+                 min_sell: &mut Decimal,
+                 data: ResponseData) {
     let mut trace_stack = TraceStack::default();
     trace_stack.on_network(data.time);
     trace_stack.on_before_quant();
@@ -57,7 +63,6 @@ async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, update_flag_u: &mut i64, max_
     }
 
     if data.channel == "aggTrade" {
-
         let trade: OriginalTradeBa = serde_json::from_str(data.data.as_str()).unwrap();
         let str = data.label.clone();
         let mut quant = bot_arc_clone.lock().await;
@@ -77,38 +82,17 @@ async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, update_flag_u: &mut i64, max_
         }
     } else if data.channel == "bookTicker" {
         trace_stack.on_before_format();
-        let ticker: OriginalTicker = serde_json::from_str(data.data.as_str()).unwrap();
-        if ticker.u > *update_flag_u {
-            {
-                let mut quant = bot_arc_clone.lock().await;
-                quant._update_ticker(SpecialTicker{
-                    sell: ticker.a.clone(),
-                    buy: ticker.b.clone(),
-                    mid_price: (ticker.a + ticker.b)/Decimal::TWO,
-                }, data.label.clone());
-                let depth = vec![ticker.b, ticker.B, ticker.a, ticker.A];
-                trace_stack.on_after_format();
-                quant._update_depth(depth.clone(), data.label.clone(), trace_stack.clone());
-                quant.local_depths.insert(data.label.clone(), depth);
-            }
-        } else {
-            *update_flag_u = ticker.u;
-        }
+        // 将ticker数据转换为模拟深度
+        let special_depth = standard::handle_info::HandleSwapInfo::handle_special_ticker(BinanceSwap, data.clone());
+        trace_stack.on_after_format();
+
+        on_special_depth(bot_arc_clone, update_flag_u, data.label.clone(), trace_stack, special_depth).await;
     } else if data.channel == "depth" {
         trace_stack.on_before_format();
-        let v: Value = serde_json::from_str(data.data.clone().as_str()).unwrap();
-        let u = v["u"].as_i64().unwrap();
-        if u > *update_flag_u {
-            let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(BinanceSwap, data);
-            trace_stack.on_after_format();
-            {
-                let mut quant = bot_arc_clone.lock().await;
-                quant._update_ticker(depth.ticker.clone(), depth.name.clone());
-                quant._update_depth(depth.depth.clone(), depth.name.clone(), trace_stack.clone());
-                quant.local_depths.insert(depth.name, depth.depth);
-            }
-        } else {
-            *update_flag_u = u;
-        }
+        // 将depth数据转换为模拟深度
+        let special_depth = standard::handle_info::HandleSwapInfo::handle_special_depth(BinanceSwap, data.clone());
+        trace_stack.on_after_format();
+
+        on_special_depth(bot_arc_clone, update_flag_u, data.label.clone(), trace_stack, special_depth).await;
     }
 }

+ 68 - 71
strategy/src/bitget_spot.rs

@@ -1,89 +1,86 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::time::Duration;
 use rust_decimal::Decimal;
-use tokio::spawn;
-use tokio::sync::mpsc::channel;
 use tokio::sync::Mutex;
-use tokio::time::sleep;
-use exchanges::bitget_spot_ws::{BitgetSpotWs, BitgetSubscribeType, BitgetWsType};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
 use standard::exchange::ExchangeEnum::BitgetSpot;
 use crate::model::{OrderInfo, OriginalTradeGa};
 use crate::quant::Quant;
 
-pub async fn bitget_spot_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mutex<Quant>>, name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>) {
-    let (tx, mut rx) = channel(100);
-    let symbols_1 = symbols.clone();
-
-    // 默认开启共有订阅频道
-    let name0 = name.clone();
-    let ep0 = exchange_params.clone();
-    let t0 = tx.clone();
-    let b0 = bool_v1.clone();
-    let s0 = symbols_1.clone();
-    spawn(async move {
-        let mut bit_exc_public = BitgetSpotWs::new_label(name0, false, ep0, BitgetWsType::Public, t0);
-        bit_exc_public.set_subscribe(vec![
-            BitgetSubscribeType::PuTrade,
-            BitgetSubscribeType::PuBooks5
-        ]);
-        bit_exc_public.custom_subscribe(b0, s0).await;
-    });
-
-    // 交易交易所需要再开一个私有频道
-    if type_num == 1 {
-        let name1 = name.clone();
-        let ep1 = exchange_params.clone();
-        let t1 = tx.clone();
-        let b1 = bool_v1.clone();
-        let s1 = symbols_1.clone();
-        spawn( async move {
-            let mut bit_exc_private = BitgetSpotWs::new_label(name1, false, ep1, BitgetWsType::Private, t1);
-            // 交易
-            bit_exc_private.set_subscribe(vec![
-                BitgetSubscribeType::PrAccount,
-                BitgetSubscribeType::PrOrders
-            ]);
-            bit_exc_private.custom_subscribe(b1, s1).await;
-        });
-    }
+#[allow(dead_code)]
+pub async fn bitget_spot_run(_bool_v1 :Arc<AtomicBool>, _type_num: i8, _quant_arc: Arc<Mutex<Quant>>, _name: String, _symbols: Vec<String>, _exchange_params: BTreeMap<String, String>) {
+    // let (tx, mut rx) = channel(100);
+    // let symbols_1 = symbols.clone();
+    //
+    // // 默认开启共有订阅频道
+    // let name0 = name.clone();
+    // let ep0 = exchange_params.clone();
+    // let t0 = tx.clone();
+    // let b0 = bool_v1.clone();
+    // let s0 = symbols_1.clone();
+    // spawn(async move {
+    //     let mut bit_exc_public = BitgetSpotWs::new_label(name0, false, ep0, BitgetWsType::Public, t0);
+    //     bit_exc_public.set_subscribe(vec![
+    //         BitgetSubscribeType::PuTrade,
+    //         BitgetSubscribeType::PuBooks5
+    //     ]);
+    //     bit_exc_public.custom_subscribe(b0, s0).await;
+    // });
+    //
+    // // 交易交易所需要再开一个私有频道
+    // if type_num == 1 {
+    //     let name1 = name.clone();
+    //     let ep1 = exchange_params.clone();
+    //     let t1 = tx.clone();
+    //     let b1 = bool_v1.clone();
+    //     let s1 = symbols_1.clone();
+    //     spawn( async move {
+    //         let mut bit_exc_private = BitgetSpotWs::new_label(name1, false, ep1, BitgetWsType::Private, t1);
+    //         // 交易
+    //         bit_exc_private.set_subscribe(vec![
+    //             BitgetSubscribeType::PrAccount,
+    //             BitgetSubscribeType::PrOrders
+    //         ]);
+    //         bit_exc_private.custom_subscribe(b1, s1).await;
+    //     });
+    // }
 
     // 新增获取余额的协程
-    let account_quant_arc = quant_arc.clone();
-    spawn(async move {
-        loop {
-            // 每30秒重新获取一次
-            sleep(Duration::from_secs(30)).await;
-
-            {
-                let mut quant = account_quant_arc.lock().await;
-                quant.update_equity_rest_spot().await;
-            }
-        }
-    });
-
-    spawn(async move {
-        let ct_val = quant_arc.clone().lock().await.platform_rest.get_self_market().ct_val;
-        // trade
-        let mut max_buy = Decimal::ZERO;
-        let mut min_sell = Decimal::ZERO;
-
-        loop {
-            sleep(Duration::from_millis(1)).await;
-
-            match rx.try_recv() {
-                Ok(data) => {
-                    on_data(quant_arc.clone(), ct_val, &mut max_buy, &mut min_sell, data).await;
-                },
-                Err(_e) => {}
-            }
-        }
-    });
+    // let account_quant_arc = quant_arc.clone();
+    // spawn(async move {
+    //     loop {
+    //         // 每30秒重新获取一次
+    //         sleep(Duration::from_secs(30)).await;
+    //
+    //         {
+    //             let mut quant = account_quant_arc.lock().await;
+    //             quant.update_equity_rest_spot().await;
+    //         }
+    //     }
+    // });
+    //
+    // spawn(async move {
+    //     let ct_val = quant_arc.clone().lock().await.platform_rest.get_self_market().ct_val;
+    //     // trade
+    //     let mut max_buy = Decimal::ZERO;
+    //     let mut min_sell = Decimal::ZERO;
+    //
+    //     loop {
+    //         sleep(Duration::from_millis(1)).await;
+    //
+    //         match rx.try_recv() {
+    //             Ok(data) => {
+    //                 on_data(quant_arc.clone(), ct_val, &mut max_buy, &mut min_sell, data).await;
+    //             },
+    //             Err(_e) => {}
+    //         }
+    //     }
+    // });
 }
 
+#[allow(dead_code)]
 async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, ct_val: Decimal, max_buy: &mut Decimal, min_sell: &mut Decimal, data: ResponseData) {
     let mut trace_stack = TraceStack::default();
 

+ 30 - 15
strategy/src/exchange_disguise.rs

@@ -1,12 +1,12 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
+use rust_decimal::Decimal;
 use tokio::sync::Mutex;
-use crate::binance_spot::reference_binance_spot_run;
+use global::trace_stack::TraceStack;
+use standard::SpecialDepth;
 use crate::binance_usdt_swap::reference_binance_swap_run;
-use crate::bitget_spot::bitget_spot_run;
 use crate::gate_swap::gate_swap_run;
-use crate::kucoin_spot::kucoin_spot_run;
 use crate::kucoin_swap::kucoin_swap_run;
 use crate::quant::Quant;
 
@@ -19,9 +19,9 @@ pub async fn run_transactional_exchange(bool_v1 :Arc<AtomicBool>, exchange_name:
         "kucoin_usdt_swap" => {
             kucoin_swap_run(bool_v1, 1i8, quant_arc, name, symbols, exchange_params).await;
         },
-        "bitget_spot" => {
-            bitget_spot_run(bool_v1,1i8, quant_arc, name, symbols, exchange_params).await;
-        }
+        // "bitget_spot" => {
+        //     bitget_spot_run(bool_v1,1i8, quant_arc, name, symbols, exchange_params).await;
+        // }
         _ => {
             let msg = format!("不支持的交易交易所:{}", exchange_name);
             panic!("{}", msg);
@@ -35,21 +35,21 @@ pub async fn run_reference_exchange(bool_v1 :Arc<AtomicBool>, exchange_name: Str
         "binance_usdt_swap" => {
             reference_binance_swap_run(bool_v1, quant_arc, name, symbols, exchange_params).await;
         },
-        "binance_spot" => {
-            reference_binance_spot_run(bool_v1, quant_arc, name, symbols, exchange_params).await;
-        },
+        // "binance_spot" => {
+        //     reference_binance_spot_run(bool_v1, quant_arc, name, symbols, exchange_params).await;
+        // },
         "gate_usdt_swap" => {
             gate_swap_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
         },
         "kucoin_usdt_swap" => {
             kucoin_swap_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
         },
-        "kucoin_spot" => {
-            kucoin_spot_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
-        },
-        "bitget_spot" => {
-            bitget_spot_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
-        },
+        // "kucoin_spot" => {
+        //     kucoin_spot_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
+        // },
+        // "bitget_spot" => {
+        //     bitget_spot_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
+        // },
         _ => {
             let msg = format!("不支持的参考交易所:{}", exchange_name);
             panic!("{}", msg);
@@ -57,3 +57,18 @@ pub async fn run_reference_exchange(bool_v1 :Arc<AtomicBool>, exchange_name: Str
     }
 }
 
+pub async fn on_special_depth(bot_arc: Arc<Mutex<Quant>>,
+                              update_flag_u: &mut Decimal,
+                              label: String,
+                              trace_stack: TraceStack,
+                              special_depth: SpecialDepth) {
+    if special_depth.t > *update_flag_u {
+        *update_flag_u = special_depth.t;
+        let mut quant = bot_arc.lock().await;
+        quant._update_ticker(special_depth.ticker, label.clone());
+        quant._update_depth(special_depth.depth.clone(), label.clone(), trace_stack);
+        quant.local_depths.insert(special_depth.name, special_depth.depth);
+    }
+}
+
+pub async fn on_trade() {}

+ 55 - 30
strategy/src/gate_swap.rs

@@ -1,16 +1,14 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::time::Duration;
+use futures_util::StreamExt;
 use rust_decimal::Decimal;
 use serde_json::Value;
-use tracing::{info};
 use tokio::spawn;
-use tokio::sync::mpsc::channel;
 use tokio::sync::Mutex;
-use tokio::time::sleep;
+use tracing::info;
 use exchanges::gate_swap_rest::GateSwapRest;
-use exchanges::gate_swap_ws::{GateSubscribeType, GateSwapWs, GateWsType};
+use exchanges::gate_swap_ws::{GateSwapLogin, GateSwapSubscribeType, GateSwapWs, GateSwapWsType};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
 use standard::exchange::ExchangeEnum::GateSwap;
@@ -18,14 +16,18 @@ use crate::model::{OrderInfo, OriginalTradeGa};
 use crate::quant::Quant;
 
 // 1交易、0参考 gate 合约 启动
-pub async fn gate_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mutex<Quant>>, name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>){
-    let (tx, mut rx) = channel(100);
+#[allow(dead_code)]
+pub async fn gate_swap_run(bool_v1: Arc<AtomicBool>, type_num: i8,
+                           quant_arc: Arc<Mutex<Quant>>, name: String,
+                           symbols: Vec<String>, exchange_params: BTreeMap<String, String>) {
+    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
+
     let mut gate_exc = GateSwapRest::new(false, exchange_params.clone());
     let mut user_id= "".to_string();
-    let symbols_one = symbols.clone();
 
     // 交易
-    if type_num == 1{
+    if type_num == 1 {
         // 获取user_id
         let res_data = gate_exc.wallet_fee().await;
         assert_eq!(res_data.code, "200", "获取gate交易所参数 user_id 失败, 启动失败!");
@@ -35,26 +37,35 @@ pub async fn gate_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Ar
         user_id = wallet_obj["user_id"].to_string();
     }
 
-    spawn( async move {
-        let mut gate_exc = GateSwapWs::new_label(name, false, exchange_params,
-                                                 GateWsType::PublicAndPrivate("usdt".to_string()), tx);
+    let write_tx_am = Arc::new(Mutex::new(write_tx));
+    let symbols_clone = symbols.clone();
+    spawn(async move {
+        let mut ws;
         // 交易
         if type_num == 1 {
-            gate_exc.set_subscribe(vec![
-                GateSubscribeType::PuFuturesTrades,
-                GateSubscribeType::PuFuturesOrderBook,
-                GateSubscribeType::PrFuturesOrders(user_id.clone()),
-                GateSubscribeType::PrFuturesPositions(user_id.clone()),
-                GateSubscribeType::PrFuturesBalances(user_id.clone()),
+            let login_param = parse_btree_map_to_gate_swap_login(exchange_params);
+            ws = GateSwapWs::new_label(name.clone(), false, Some(login_param),
+                                       GateSwapWsType::PublicAndPrivate("usdt".to_string()));
+            ws.set_subscribe(vec![
+                GateSwapSubscribeType::PuFuturesTrades,
+                GateSwapSubscribeType::PuFuturesOrderBook,
+                GateSwapSubscribeType::PrFuturesOrders(user_id.clone()),
+                GateSwapSubscribeType::PrFuturesPositions(user_id.clone()),
+                GateSwapSubscribeType::PrFuturesBalances(user_id.clone()),
             ]);
         } else { // 参考
-            gate_exc.set_subscribe(vec![
-                GateSubscribeType::PuFuturesTrades,
-                GateSubscribeType::PuFuturesOrderBook
+            ws = GateSwapWs::new_label(name.clone(), false, None,
+                                       GateSwapWsType::PublicAndPrivate("usdt".to_string()));
+            ws.set_subscribe(vec![
+                GateSwapSubscribeType::PuFuturesTrades,
+                GateSwapSubscribeType::PuFuturesOrderBook
             ]);
         }
-        gate_exc.custom_subscribe(bool_v1,symbols_one).await;
+
+        ws.set_symbols(symbols_clone);
+        ws.ws_connect_async(bool_v1, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
     });
+
     spawn(async move {
         let bot_arc_clone = Arc::clone(&quant_arc);
         let multiplier = bot_arc_clone.lock().await.platform_rest.get_self_market().amount_size;
@@ -62,20 +73,27 @@ pub async fn gate_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Ar
         // trade
         let mut max_buy = Decimal::ZERO;
         let mut min_sell = Decimal::ZERO;
-        loop {
-            sleep(Duration::from_millis(1)).await;
 
-            match rx.try_recv() {
-                Ok(data) => {
-                    on_data(bot_arc_clone.clone(), multiplier, run_symbol.clone(), &mut max_buy, &mut min_sell, data).await;
-                },
-                Err(_e) => {}
+        loop {
+            if let Some(data) = read_rx.next().await {
+                on_data(bot_arc_clone.clone(),
+                        multiplier,
+                        run_symbol.clone(),
+                        &mut max_buy,
+                        &mut min_sell,
+                        data).await;
             }
         }
     });
 }
 
-async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, multiplier: Decimal, run_symbol: String, max_buy: &mut Decimal, min_sell: &mut Decimal, data: ResponseData) {
+#[allow(dead_code)]
+async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>,
+                 multiplier: Decimal,
+                 run_symbol: String,
+                 max_buy: &mut Decimal,
+                 min_sell: &mut Decimal,
+                 data: ResponseData) {
     let mut trace_stack = TraceStack::default();
     trace_stack.on_network(data.time);
     trace_stack.on_before_quant();
@@ -156,3 +174,10 @@ async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, multiplier: Decimal, run_symb
         quant.max_buy_min_sell_cache.insert(data.label, vec![*max_buy, *min_sell]);
     }
 }
+
+fn parse_btree_map_to_gate_swap_login(exchange_params: BTreeMap<String, String>) -> GateSwapLogin {
+    GateSwapLogin {
+        api_key: exchange_params.get("access_key").unwrap().clone(),
+        secret: exchange_params.get("secret_key").unwrap().clone()
+    }
+}

+ 41 - 44
strategy/src/kucoin_spot.rs

@@ -1,61 +1,58 @@
 use std::collections::BTreeMap;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::time::Duration;
 use rust_decimal::Decimal;
-use tokio::spawn;
-use tokio::sync::mpsc::channel;
 use tokio::sync::Mutex;
-use tokio::time::sleep;
-use exchanges::kucoin_spot_ws::{KucoinSubscribeType, KucoinSpotWs, KucoinWsType};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
 use standard::exchange::ExchangeEnum::KucoinSpot;
 use crate::quant::Quant;
 
 // 1交易、0参考 kucoin 现货 启动
-pub async fn kucoin_spot_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mutex<Quant>>, name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>) {
-    let (tx, mut rx) = channel(100);
-    let symbols_clone = symbols.clone();
-    let mut symbol_arr = Vec::new();
-    for symbol in symbols_clone {
-        let symbol_mapper = standard::utils::symbol_enter_mapper(KucoinSpot,symbol.as_str());
-        let new_symbol = symbol_mapper.replace("_", "-").to_uppercase();
-        symbol_arr.push(new_symbol);
-    }
-    spawn( async move {
-        let mut kucoin_exc;
-        kucoin_exc = KucoinSpotWs::new_label(name, false, exchange_params, KucoinWsType::Public, tx).await;
-        if type_num == 0 {
-            kucoin_exc.set_subscribe(vec![
-                KucoinSubscribeType::PuSpotMarketLevel2Depth50,
-                KucoinSubscribeType::PuMarketTicker,
-            ]);
-            kucoin_exc.custom_subscribe(bool_v1, symbol_arr).await;
-        }
-    });
-
-    spawn(async move {
-        let bot_arc_clone = Arc::clone(&quant_arc);
-        // let run_symbol = symbols.clone()[0].clone();
-        // trade
-        let mut max_buy = Decimal::ZERO;
-        let mut min_sell = Decimal::ZERO;
-        // let multiplier = bot_arc_clone.lock().await.platform_rest.get_self_market().ct_val;
-        let multiplier = Decimal::ONE;
-        loop {
-            sleep(Duration::from_millis(1)).await;
+#[allow(dead_code)]
+pub async fn kucoin_spot_run(_bool_v1 :Arc<AtomicBool>, _type_num: i8, _quant_arc: Arc<Mutex<Quant>>, _name: String, _symbols: Vec<String>, _exchange_params: BTreeMap<String, String>) {
+    // let (tx, mut rx) = channel(100);
+    // let symbols_clone = symbols.clone();
+    // let mut symbol_arr = Vec::new();
+    // for symbol in symbols_clone {
+    //     let symbol_mapper = standard::utils::symbol_enter_mapper(KucoinSpot,symbol.as_str());
+    //     let new_symbol = symbol_mapper.replace("_", "-").to_uppercase();
+    //     symbol_arr.push(new_symbol);
+    // }
+    // spawn( async move {
+    //     let mut kucoin_exc;
+    //     kucoin_exc = KucoinSpotWs::new_label(name, false, exchange_params, KucoinWsType::Public, tx).await;
+    //     if type_num == 0 {
+    //         kucoin_exc.set_subscribe(vec![
+    //             KucoinSubscribeType::PuSpotMarketLevel2Depth50,
+    //             KucoinSubscribeType::PuMarketTicker,
+    //         ]);
+    //         kucoin_exc.custom_subscribe(bool_v1, symbol_arr).await;
+    //     }
+    // });
 
-            match rx.try_recv() {
-                Ok(data) => {
-                    on_data(bot_arc_clone.clone(), multiplier, &mut max_buy, &mut min_sell, data).await;
-                },
-                Err(_e) => { }
-            }
-        }
-    });
+    // spawn(async move {
+    //     let bot_arc_clone = Arc::clone(&quant_arc);
+    //     // let run_symbol = symbols.clone()[0].clone();
+    //     // trade
+    //     let mut max_buy = Decimal::ZERO;
+    //     let mut min_sell = Decimal::ZERO;
+    //     // let multiplier = bot_arc_clone.lock().await.platform_rest.get_self_market().ct_val;
+    //     let multiplier = Decimal::ONE;
+    //     loop {
+    //         sleep(Duration::from_millis(1)).await;
+    //
+    //         match rx.try_recv() {
+    //             Ok(data) => {
+    //                 on_data(bot_arc_clone.clone(), multiplier, &mut max_buy, &mut min_sell, data).await;
+    //             },
+    //             Err(_e) => { }
+    //         }
+    //     }
+    // });
 }
 
+#[allow(dead_code)]
 async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, _multiplier: Decimal, _max_buy: &mut Decimal, _min_sell: &mut Decimal, data: ResponseData) {
     let mut trace_stack = TraceStack::default();
     trace_stack.on_network(data.time);

+ 87 - 59
strategy/src/kucoin_swap.rs

@@ -1,54 +1,36 @@
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap};
 use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
+use std::sync::atomic::{AtomicBool};
 use std::time::Duration;
+use futures_util::StreamExt;
+
 use rust_decimal::Decimal;
+use tokio_tungstenite::tungstenite::Message;
 use tokio::spawn;
-use tokio::sync::mpsc::channel;
 use tokio::sync::Mutex;
 use tokio::time::sleep;
-use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
+
+use exchanges::kucoin_swap_ws::{KucoinSwapLogin, KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
 use exchanges::response_base::ResponseData;
 use global::trace_stack::TraceStack;
 use standard::exchange::ExchangeEnum::KucoinSwap;
+use crate::exchange_disguise::on_special_depth;
+
 use crate::model::{OrderInfo, OriginalTradeGa};
 use crate::quant::Quant;
 
 // 1交易、0参考 kucoin 合约 启动
-pub async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mutex<Quant>>, name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>){
-    let (tx, mut rx) = channel(100);
+pub async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mutex<Quant>>, 
+                             name: String, symbols: Vec<String>, exchange_params: BTreeMap<String, String>) {
     let symbols_clone = symbols.clone();
     let mut symbol_arr = Vec::new();
-    for symbol in symbols_clone{
+    for symbol in symbols_clone {
         let symbol_mapper = standard::utils::symbol_enter_mapper(KucoinSwap,symbol.as_str());
         let new_symbol = symbol_mapper.replace("_", "").to_uppercase() + "M";
         symbol_arr.push(new_symbol);
     }
-    spawn( async move {
-        let mut kucoin_exc;
-        // 交易
-        if type_num == 1 {
-            kucoin_exc = KucoinSwapWs::new_label(name, false, exchange_params, KucoinWsType::Private, tx).await;
-            kucoin_exc.set_subscribe(vec![
-                KucoinSubscribeType::PuContractMarketLevel2Depth50,
-                // KucoinSubscribeType::PuContractMarkettickerV2,
-                KucoinSubscribeType::PrContractAccountWallet,
-                KucoinSubscribeType::PrContractPosition,
-                KucoinSubscribeType::PrContractMarketTradeOrders
-            ]);
-        } else { // 参考
-            kucoin_exc = KucoinSwapWs::new_label(name, false, exchange_params, KucoinWsType::Public, tx).await;
-            kucoin_exc.set_subscribe(vec![
-                KucoinSubscribeType::PuContractMarketLevel2Depth50,
-                // TODO: python注释掉了
-                // KucoinSubscribeType::PuContractMarkettickerV2,
-                KucoinSubscribeType::PuContractMarketExecution
-            ]);
-        }
-        kucoin_exc.custom_subscribe(bool_v1, symbol_arr).await;
-    });
 
-    // 新增获取余额的协程
+    // 新增定期获取余额的协程
     let account_quant_arc = quant_arc.clone();
     spawn(async move {
         loop {
@@ -63,26 +45,71 @@ pub async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc:
     });
 
     spawn(async move {
-        let bot_arc_clone = Arc::clone(&quant_arc);
-        // let run_symbol = symbols.clone()[0].clone();
-        // trade
-        let mut max_buy = Decimal::ZERO;
-        let mut min_sell = Decimal::ZERO;
-        let multiplier = bot_arc_clone.lock().await.platform_rest.get_self_market().ct_val;
-        loop {
-            sleep(Duration::from_millis(1)).await;
+        //创建读写通道
+        let (write_tx, write_rx) = futures_channel::mpsc::unbounded::<Message>();
+        let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
 
-            match rx.try_recv() {
-                Ok(data) => {
-                    on_data(bot_arc_clone.clone(), multiplier, &mut max_buy, &mut min_sell, data).await;
-                },
-                Err(_e) => { }
+        spawn( async move {
+            let mut kucoin_exc;
+            //模拟业务场景 开启链接
+            let bool_v1_clone = Arc::clone(&bool_v1);
+            let write_tx_am = Arc::new(Mutex::new(write_tx));
+
+            // 交易
+            if type_num == 1 {
+                let login_params = parse_btree_map_to_kucoin_swap_login(exchange_params);
+                kucoin_exc = KucoinSwapWs::new_label(name.clone(), false, Option::from(login_params), KucoinSwapWsType::Private).await;
+                kucoin_exc.set_subscribe(vec![
+                    KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
+                    // KucoinSwapSubscribeType::PuContractMarkettickerV2,
+                    // KucoinSwapSubscribeType::PrContractAccountWallet,
+                    KucoinSwapSubscribeType::PrContractPosition,
+                    KucoinSwapSubscribeType::PrContractMarketTradeOrders
+                ]);
+            } else { // 参考
+                kucoin_exc = KucoinSwapWs::new_label(name.clone(), false, None, KucoinSwapWsType::Public).await;
+                kucoin_exc.set_subscribe(vec![
+                    KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
+                    // python注释掉了
+                    // KucoinSwapSubscribeType::PuContractMarkettickerV2,
+                    KucoinSwapSubscribeType::PuContractMarketExecution
+                ]);
             }
-        }
+
+            kucoin_exc.set_symbols(symbol_arr);
+            kucoin_exc.ws_connect_async(bool_v1_clone, &write_tx_am, write_rx, read_tx).await.unwrap();
+        });
+
+        // 数据处理协程
+        spawn(async move {
+            let bot_arc_clone = Arc::clone(&quant_arc);
+
+            // ticker
+            let mut update_flag_u = Decimal::ZERO;
+            let mut max_buy = Decimal::ZERO;
+            let mut min_sell = Decimal::ZERO;
+            let multiplier = bot_arc_clone.lock().await.platform_rest.get_self_market().ct_val;
+
+            loop {
+                if let Some(data) = read_rx.next().await {
+                    on_data(bot_arc_clone.clone(),
+                            &mut update_flag_u,
+                            multiplier,
+                            &mut max_buy,
+                            &mut min_sell,
+                            data).await;
+                }
+            }
+        });
     });
 }
 
-async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, multiplier: Decimal, max_buy: &mut Decimal, min_sell: &mut Decimal, data: ResponseData) {
+async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>,
+                 update_flag_u: &mut Decimal,
+                 multiplier: Decimal,
+                 max_buy: &mut Decimal,
+                 min_sell: &mut Decimal,
+                 data: ResponseData) {
     let mut trace_stack = TraceStack::default();
     trace_stack.on_network(data.time);
     trace_stack.on_before_quant();
@@ -92,21 +119,14 @@ async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, multiplier: Decimal, max_buy:
     }
     if data.channel == "level2" {
         trace_stack.on_before_format();
-        let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(KucoinSwap,data);
+        let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(KucoinSwap,data.clone());
         trace_stack.on_after_format();
-        {
-            let mut quant = bot_arc_clone.lock().await;
-            // time_delay.quant_start = Utc::now().timestamp_micros();
-            quant._update_ticker(depth.ticker.clone(), depth.name.clone());
-            quant._update_depth(depth.depth.clone(), depth.name.clone(), trace_stack.clone());
-            quant.local_depths.insert(depth.name, depth.depth);
-        }
+
+        on_special_depth(bot_arc_clone, update_flag_u, data.label, trace_stack, depth).await
     } else if data.channel == "tickerV2" {
-        let ticker = standard::handle_info::HandleSwapInfo::handle_special_ticker(KucoinSwap, data);
-        {
-            let mut quant = bot_arc_clone.lock().await;
-            quant._update_ticker(ticker.ticker, ticker.name);
-        }
+        let depth = standard::handle_info::HandleSwapInfo::handle_special_ticker(KucoinSwap, data.clone());
+
+        on_special_depth(bot_arc_clone, update_flag_u, data.label, trace_stack, depth).await
     } else if data.channel == "availableBalance.change" {
         // 取消原有推送解析,因为推送的信息不准确
         // let account = standard::handle_info::HandleSwapInfo::handle_account_info(KucoinSwap, data, run_symbol.clone());
@@ -169,3 +189,11 @@ async fn on_data(bot_arc_clone: Arc<Mutex<Quant>>, multiplier: Decimal, max_buy:
         quant.max_buy_min_sell_cache.insert(data.label, vec![*max_buy, *min_sell]);
     }
 }
+
+fn parse_btree_map_to_kucoin_swap_login(exchange_params: BTreeMap<String, String>) -> KucoinSwapLogin {
+    KucoinSwapLogin {
+        access_key: exchange_params.get("access_key").unwrap().clone(),
+        secret_key: exchange_params.get("secret_key").unwrap().clone(),
+        pass_key: exchange_params.get("pass_key").unwrap().clone(),
+    }
+}

+ 6 - 7
strategy/src/predictor.rs

@@ -1,7 +1,7 @@
 use std::collections::BTreeMap;
 use rust_decimal::prelude::*;
 use rust_decimal_macros::dec;
-use tracing::{debug};
+use tracing::{instrument};
 use standard::Ticker;
 use global::public_params;
 
@@ -48,6 +48,7 @@ impl Predictor {
     }
 
     // 计算任务,python里写作processer,是个错误的单词
+    #[instrument(skip(self), level="TRACE")]
     fn processor(&mut self) {
         let last_market_info = self.market_info_list.last().unwrap();
 
@@ -55,8 +56,6 @@ impl Predictor {
         let bid_price = last_market_info[public_params::BID_PRICE_INDEX];
         let ask_price = last_market_info[public_params::ASK_PRICE_INDEX];
         let mid_price = (bid_price + ask_price) * dec!(0.5);
-        debug!(?last_market_info);
-        debug!(?bid_price, ?ask_price, ?mid_price);
         self.mid_price_list.push(mid_price);
 
         // 更新参考ref_mid_price
@@ -65,11 +64,9 @@ impl Predictor {
             let ref_bid_price = last_market_info[public_params::LENGTH*(1+ref_index)+public_params::BID_PRICE_INDEX];
             let ref_ask_price = last_market_info[public_params::LENGTH*(1+ref_index)+public_params::ASK_PRICE_INDEX];
             let ref_mid_price = (ref_bid_price + ref_ask_price) * dec!(0.5);
-            debug!(?ref_bid_price, ?ref_ask_price, ?ref_mid_price);
             // 依照交易所次序添加到ref_mid_price_per_exchange中
             ref_mid_price_per_exchange.push(ref_mid_price);
         }
-        debug!(?ref_mid_price_per_exchange);
         self.ref_mid_price_per_exchange_per_frame.push(ref_mid_price_per_exchange);
 
         // 价差更新
@@ -77,6 +74,7 @@ impl Predictor {
     }
 
     // 更新平均价差,_update_avg_spread
+    #[instrument(skip(self), level="TRACE")]
     fn update_avg_spread(&mut self) {
         let last_ref_mid_price_per_exchange = self.ref_mid_price_per_exchange_per_frame.last().unwrap();
         let mid_price_last = self.mid_price_list.last().unwrap();
@@ -100,6 +98,7 @@ impl Predictor {
     }
 
     // 长度限定
+    #[instrument(skip(self), level="TRACE")]
     fn check_length(&mut self) {
         // 市场汇总信息长度限定
         if self.market_info_list.len() > self.data_length_max {
@@ -116,8 +115,8 @@ impl Predictor {
     }
 
     // 市场信息处理器,也是python里的onTime方法
+    #[instrument(skip(self, new_market_info), level="TRACE")]
     pub fn market_info_handler(&mut self, new_market_info: &Vec<Decimal>) {
-        debug!(?new_market_info);
         // 空数据不处理
         if new_market_info.len() == 0 {
             return;
@@ -132,6 +131,7 @@ impl Predictor {
     }
 
     // 获取预定价格, 也就是python的Get_ref函数
+    #[instrument(skip(self, ref_ticker_map), level="TRACE")]
     pub fn get_ref_price(&mut self, ref_ticker_map: &BTreeMap<String, Ticker>) -> Vec<Vec<Decimal>> {
         let mut ref_price_list = vec![];
         let ref_exchange_names: Vec<_> = ref_ticker_map.keys().collect();
@@ -146,7 +146,6 @@ impl Predictor {
 
             ref_price_list.push(vec![ref_bid_price, ref_ask_price]);
         }
-        debug!(?ref_price_list);
 
         return ref_price_list;
     }

+ 49 - 28
strategy/src/quant.rs

@@ -14,7 +14,7 @@ use tokio::sync::mpsc::{Sender};
 use tokio::sync::{Mutex};
 use tokio::task::JoinHandle;
 use tokio::time::sleep;
-use tracing::{debug, error, info, warn};
+use tracing::{error, info, warn, instrument};
 use global::params::Params;
 use global::public_params::{ASK_PRICE_INDEX, BID_PRICE_INDEX, LENGTH};
 use global::trace_stack::TraceStack;
@@ -238,11 +238,7 @@ impl Quant {
             let market_update_interval_key = tickers_key.clone();
             let max_buy_min_sell_cache_key = tickers_key.clone();
 
-            quant_obj.tickers.insert(tickers_key, SpecialTicker {
-                sell: Default::default(),
-                buy: Default::default(),
-                mid_price: Default::default(),
-            });
+            quant_obj.tickers.insert(tickers_key, SpecialTicker::new());
             quant_obj.ref_name.push(ref_name_element);
             quant_obj.depths.insert(depths_key, Default::default());
             quant_obj.market_update_time.insert(market_update_time_key, Default::default());
@@ -258,11 +254,7 @@ impl Quant {
         quant_obj.trade_name = name;
         quant_obj.market_update_time.insert(market_update_time_key, Default::default());
         quant_obj.market_update_interval.insert(market_update_interval_key, Default::default());
-        quant_obj.tickers.insert(tickers_key, SpecialTicker {
-            sell: Default::default(),
-            buy: Default::default(),
-            mid_price: Default::default(),
-        });
+        quant_obj.tickers.insert(tickers_key, SpecialTicker::new());
         quant_obj.depths.insert(depths_key, Default::default());
         quant_obj.max_buy_min_sell_cache.insert(max_buy_min_sell_cache_key, vec![Decimal::ZERO, Decimal::ZERO]);
         // broker.newWs
@@ -287,12 +279,14 @@ impl Quant {
         return quant_obj;
     }
 
+    #[instrument(skip(self, data, trace_stack), level="TRACE")]
     pub fn update_order(&mut self, data: Vec<OrderInfo>, trace_stack: TraceStack) {
         for order in data {
             self.update_local_order(order, trace_stack.clone());
         }
     }
 
+    #[instrument(skip(self, data, trace_stack), level="TRACE")]
     pub fn update_local_order(&mut self, data: OrderInfo, mut trace_stack: TraceStack) {
         // if data.filled != Decimal::ZERO {
         //     info!("\n\n");
@@ -328,6 +322,7 @@ impl Quant {
         // 新增订单推送 仅需要cid oid信息
         if data.status == "NEW" {
             // 更新oid信息 更新订单 loceltime信息(尤其是查单返回new的情况 必须更新 否则会误触发风控)
+
             if self.local_orders.contains_key(&data.client_id) {
                 let mut order_info = self.local_orders.get(&data.client_id).unwrap().clone();
                 order_info.order_id = data.order_id;
@@ -339,23 +334,11 @@ impl Quant {
             if self.local_cancel_log.contains_key(&data.client_id) {
                 self.local_cancel_log.remove(&data.client_id);
             }
-            if self.local_orders.contains_key(&data.client_id) {
-                // 成交数量不为空,则打印耗时追踪
-                if data.filled > Decimal::ZERO {
-                    let local_order = self.local_orders.get(&data.client_id).unwrap();
-                    info!("订单耗时追踪:{:?}", local_order.trace_stack.to_string());
-                }
-
-                debug!("删除本地订单, client_id:{:?}", data);
-                self.local_orders.remove(&data.client_id);
-            } else {
-                debug!("该订单不在本地挂单表中, order:{:?}", data);
-            }
             // 在cid缓存队列中 说明是本策略的订单
             if self.local_orders_backup.contains_key(&data.client_id) {
                 // 不在已处理cid缓存队列中 说明还没参与过仓位计算 则执行订单计算
                 if self.handled_orders_cid.contains(&data.client_id) {
-                    debug!("订单已经参与过仓位计算 拒绝重复进行计算, 订单号:{}", data.client_id);
+                    // debug!("订单已经参与过仓位计算 拒绝重复进行计算, 订单号:{}", data.client_id);
                 } else {
                     // 添加进已处理队列
                     self.handled_orders_cid.push(data.client_id.clone());
@@ -521,13 +504,27 @@ impl Quant {
                     }
                 }
             } else {
-                debug!("订单不属于本策略 拒绝进行仓位计算: {}", data.client_id);
+                // debug!("订单不属于本策略 拒绝进行仓位计算: {}", data.client_id);
+            }
+
+            if self.local_orders.contains_key(&data.client_id) {
+                // 成交数量不为空,则打印耗时追踪
+                if data.filled > Decimal::ZERO {
+                    let local_order = self.local_orders.get(&data.client_id).unwrap();
+                    info!("订单耗时追踪:{:?}", local_order.trace_stack.to_string());
+                }
+
+                // debug!("删除本地订单, client_id:{:?}", data);
+                self.local_orders.remove(&data.client_id);
+            } else {
+                // debug!("该订单不在本地挂单表中, order:{:?}", data);
             }
         } else {
             error!("未知的订单事件类型:{:?}", data);
         }
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub fn _print_local_trades_summary(&mut self) {
         // 计算本地累计利润
         let local_buy_amount = self.local_buy_amount.round_dp(5);
@@ -545,6 +542,7 @@ impl Quant {
     }
 
     // 检测初始数据是否齐全
+    #[instrument(skip(self), level="TRACE")]
     pub fn check_ready(&mut self) {
         // 检查 ticker 行情
         for i in &self.ref_name {
@@ -581,6 +579,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self, msg), level="TRACE")]
     pub fn log_ready_status(&mut self, msg: String) {
         // 隔一会再打印未准备就绪的台词
         let now_timestamp = Utc::now().timestamp_millis();
@@ -590,7 +589,9 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self, depth, name, trace_stack), level="TRACE")]
     pub fn _update_depth(&mut self, depth: Vec<Decimal>, name: String, mut trace_stack: TraceStack) {
+        // info!(?depth, ?name);
         trace_stack.on_depth();
 
         // 要从回调传入的深度信息中获取data.name
@@ -652,7 +653,7 @@ impl Quant {
                 let orders = self.strategy.on_time(&self.trade_msg);
                 trace_stack.on_after_strategy();
                 if orders.is_not_empty() {
-                    debug!("触发onTick");
+                    // debug!("触发onTick");
                     self._update_local_orders(&orders);
                     //异步交易所处理订单信号
                     let mut platform_rest_fb = self.platform_rest.clone_box();
@@ -670,6 +671,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self, data), level="TRACE")]
     pub fn update_position(&mut self, data: Vec<Position>) {
         if data.is_empty() {
             return;
@@ -692,6 +694,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self, data, name), level="TRACE")]
     pub fn _update_ticker(&mut self, data: SpecialTicker, name: String) {
         // let spread = data.buy - data.sell;
         // if spread > self.predictor.max_spread || self.predictor.max_spread == Decimal::ZERO{
@@ -711,6 +714,7 @@ impl Quant {
 
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub fn on_agg_market(&mut self) {
         /* 处理聚合行情
            1. 获取聚合行情
@@ -725,6 +729,7 @@ impl Quant {
         self.predictor.market_info_handler(&self.trade_msg.market);
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub fn update_trade_msg(&mut self) {
         // 更新保证金
         self.trade_msg.cash = self.local_cash.round_dp(10);
@@ -762,6 +767,7 @@ impl Quant {
     }
 
     // 本地记录所有报单信息
+    #[instrument(skip(self, orders), level="TRACE")]
     pub fn _update_local_orders(&mut self, orders: &OrderCommand) {
         let mut limits = HashMap::new();
         limits.extend(orders.clone().limits_open);
@@ -822,6 +828,7 @@ impl Quant {
     }
 
     // 获取深度信息
+    #[instrument(skip(self), level="TRACE")]
     pub fn get_all_market_data(&mut self) -> Vec<Decimal> {
         // 只能定时触发 组合市场信息=交易盘口+参考盘口
         let mut market: Vec<Decimal> = Vec::new();
@@ -843,10 +850,12 @@ impl Quant {
         return market;
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn get_exchange_info(&mut self) {
         self.market = self.platform_rest.get_self_market();
     }
 
+    #[instrument(skip(self, data), level="TRACE")]
     pub fn update_equity(&mut self, data: Account) {
         /*
            更新保证金信息
@@ -859,6 +868,7 @@ impl Quant {
         self.local_cash = data.balance * self.used_pct
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn update_equity_rest_swap(&mut self) {
         match self.platform_rest.get_account().await {
             Ok(val) => {
@@ -875,6 +885,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn update_equity_rest_spot(&mut self) {
         match self.platform_rest.get_spot_account().await {
             Ok(mut val) => {
@@ -902,6 +913,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn check_risk(&mut self) {
         // 参数检查的风控
         if self.strategy.start_cash == Decimal::ZERO {
@@ -1054,6 +1066,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn buy_token(&mut self) {
         // 买入平台币
         // 获取U数量,平台币数量
@@ -1111,6 +1124,7 @@ impl Quant {
         }
     }
 
+    #[instrument(skip(self, target_hold_coin), level="TRACE")]
     pub async fn check_position(&mut self, target_hold_coin: Decimal) {
         info!("清空挂单!");
         match self.platform_rest.cancel_orders_all().await {
@@ -1137,6 +1151,7 @@ impl Quant {
         info!("遗留仓位检测完毕");
     }
 
+    #[instrument(skip(self, target_hold_coin), level="TRACE")]
     pub async fn check_position_spot(&mut self, target_hold_coin: Decimal) {
         info!("---------------------------检查遗漏仓位(现货),目标持仓:{}USDT---------------------------", target_hold_coin);
         match self.platform_rest.get_spot_account().await {
@@ -1222,6 +1237,7 @@ impl Quant {
         info!("---------------------------遗漏仓位检查完毕(现货)!-----------------------------------");
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn check_position_swap(&mut self) {
         info!("---------------------------检查遗漏仓位(合约)!-----------------------------------");
         match self.platform_rest.get_positions().await {
@@ -1300,6 +1316,7 @@ impl Quant {
     }
 
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn stop(&mut self) {
         /*
          *  停机函数
@@ -1324,6 +1341,7 @@ impl Quant {
         // info!("退出进程!");
     }
 
+    #[instrument(skip(self, delay), level="TRACE")]
     pub async fn exit(&mut self, delay: i8) {
         info!("--------------------------------------------------");
         info!("预约退出操作 delay:{}", delay);
@@ -1343,6 +1361,7 @@ impl Quant {
         info!("退出进程!");
     }
 
+    #[instrument(skip(self), level="TRACE")]
     pub async fn before_trade(&mut self) -> bool {
         sleep(Duration::from_secs(1)).await;
         // 获取市场信息
@@ -1448,6 +1467,7 @@ impl Quant {
     }
 }
 
+#[instrument(skip(quant_arc), level="TRACE")]
 pub fn run_strategy(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()> {
     return spawn(async move {
         //定期触发策略
@@ -1514,6 +1534,7 @@ pub fn run_strategy(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()> {
 }
 
 // 定期触发的系统逻辑
+#[instrument(skip(quant_arc), level="TRACE")]
 pub fn on_timer(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()> {
     let quant_arc_clone = quant_arc.clone();
 
@@ -1543,8 +1564,8 @@ pub fn on_timer(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()> {
                 // TODO quant没有rest
                 // info!("Rest报单平均延迟{}ms", quant.rest.avg_delay);
                 // info!("Rest报单最高延迟{}ms", quant.rest.max_delay);
-                for (name, interval) in &quant.market_update_interval {
-                    debug!("WS盘口{}行情平均更新间隔{}ms。", name, interval);
+                for (_name, _interval) in &quant.market_update_interval {
+                    // debug!("WS盘口{}行情平均更新间隔{}ms。", name, interval);
                 }
             }
         }

+ 68 - 72
strategy/src/strategy.rs

@@ -8,7 +8,7 @@ use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
 use rust_decimal_macros::dec;
 use crate::model::{LocalPosition, OrderInfo, TraderMsg};
 use crate::utils;
-use tracing::{info, instrument, error, debug, warn};
+use tracing::{info, error, warn, instrument};
 use global::params::Params;
 use standard::OrderCommand;
 
@@ -233,8 +233,8 @@ impl Strategy {
     // 更新当前strategy的各类信息
     #[instrument(skip(self, trader_msg), level="TRACE")]
     pub fn _update_data(&mut self, trader_msg: &TraderMsg) -> bool {
-        debug!(?self);
-        debug!(?trader_msg);
+        // debug!(?self);
+        // debug!(?trader_msg);
 
         self.local_orders.clear();
         self.local_orders = trader_msg.orders.clone();
@@ -248,7 +248,7 @@ impl Strategy {
             self.pos.short_pos = trader_msg.position.short_pos;
             self.pos.short_avg = trader_msg.position.short_avg;
         }
-        debug!(?self.pos);
+        // debug!(?self.pos);
 
         // 价格值处理
         self.bp = trader_msg.market[global::public_params::BID_PRICE_INDEX];
@@ -260,7 +260,7 @@ impl Strategy {
         } else {
             self.mp_ema = self.mp_ema * dec!(0.999) + self.mp * dec!(0.001);
         }
-        debug!(?self.bp, ?self.ap, ?self.mp, ?self.mp_ema);
+        // debug!(?self.bp, ?self.ap, ?self.mp, ?self.mp_ema);
 
         // 动态杠杆调节
         if self.mp > self.mp_ema {
@@ -268,12 +268,12 @@ impl Strategy {
         } else {
             self.adjust_lever_rate = dec!(0.8);
         }
-        debug!(?self.adjust_lever_rate);
+        // debug!(?self.adjust_lever_rate);
 
         // 当前持仓价值处理
         self.long_hold_value = self.pos.long_pos * self.mp;
         self.short_hold_value = self.pos.short_pos * self.mp;
-        debug!(?self.long_hold_value, ?self.short_hold_value);
+        // debug!(?self.long_hold_value, ?self.short_hold_value);
 
         // 分现货或合约计算最大开仓价值
         if self.exchange.contains("spot") {
@@ -283,7 +283,7 @@ impl Strategy {
             self.max_long_value = self.equity * self.lever_rate * self.adjust_lever_rate;
             self.max_short_value = self.max_long_value;
         }
-        debug!(?self.max_long_value, ?self.max_short_value, ?self.equity, ?self.lever_rate, ?self.adjust_lever_rate);
+        // debug!(?self.max_long_value, ?self.max_short_value, ?self.equity, ?self.lever_rate, ?self.adjust_lever_rate);
 
         // 做市模式识别
         if self.ref_name[self.ref_index].eq(&self.trade_name) {
@@ -291,23 +291,23 @@ impl Strategy {
         } else {
             self.maker_mode = "follow".to_string();
         }
-        debug!(?self.maker_mode);
+        // debug!(?self.maker_mode);
 
         // 参考价格
         if trader_msg.ref_price.len() == 0 {
-            debug!("参考价格还未预热完成,等待预热...");
+            info!("参考价格还未预热完成,等待预热...");
             return false;
         } else {
             self.ref_bp = trader_msg.ref_price[self.ref_index][0];
             self.ref_ap = trader_msg.ref_price[self.ref_index][1];
             self.ref_price = (self.ref_bp + self.ref_ap) * dec!(0.5);
         }
-        debug!(?self.ref_bp, ?self.ref_ap, %self.ref_price);
+        // debug!(?self.ref_bp, ?self.ref_ap, %self.ref_price);
 
         // spread
         let temp_predict = trader_msg.predict * self.predict_alpha;
         self.predict = utils::clip(temp_predict, -self.trade_open_dist, self.trade_open_dist);
-        debug!(?self.predict);
+        // debug!(?self.predict);
 
         // 计算当前账户cash和coin
         self.coin = trader_msg.coin;
@@ -316,12 +316,12 @@ impl Strategy {
         if self.equity > self.max_equity {
             self.max_equity = self.equity;
         }
-        debug!(?self.coin, ?self.cash, ?self.equity, ?self.max_equity);
+        // debug!(?self.coin, ?self.cash, ?self.equity, ?self.max_equity);
 
         // 总可开数量
         self.total_amount = self.equity * self.lever_rate * self.adjust_lever_rate / self.mp;
         self.total_amount = utils::fix_amount(self.total_amount, self.step_size);
-        debug!(?self.total_amount);
+        // debug!(?self.total_amount);
         if self.total_amount.eq(&Decimal::ZERO) {
             error!("总可开数量低于一张,请尝试加大杠杆倍数或资金!equity={}, lever_rate={}, adjust_lever_rate={}, mp={}, step_size={}",
                 self.equity, self.lever_rate, self.adjust_lever_rate, self.mp, self.step_size);
@@ -334,7 +334,7 @@ impl Strategy {
             if max_pos_rate > self.max_pos_rate {
                 self.max_pos_rate = max_pos_rate;
             }
-            debug!(?max_pos_rate, ?self.max_pos_rate);
+            // debug!(?max_pos_rate, ?self.max_pos_rate);
         }
 
         return true;
@@ -444,7 +444,7 @@ impl Strategy {
             "pk".to_string()
         ];
 
-        debug!(?self.local_orders);
+        // debug!(?self.local_orders);
         for client_id in self.local_orders.keys() {
             let order = self.local_orders.get(client_id).unwrap();
 
@@ -458,7 +458,7 @@ impl Strategy {
             let value = vec![order.client_id.clone(), order.order_id.clone()];
             command.cancel.insert(key, value);
         }
-        debug!(?command);
+        // debug!(?command);
     }
 
     // 生成各类挂单价格,原文是gen_dist
@@ -495,7 +495,7 @@ impl Strategy {
             sell_start * (Decimal::ONE + predict + close - avoid),                   // sell lower
             sell_start * (Decimal::ONE + predict + close + avoid),                   // sell upper
         ];
-        debug!(?mp, ?buy_start, ?sell_start, ?avoid, ?close_dist);
+        // debug!(?mp, ?buy_start, ?sell_start, ?avoid, ?close_dist);
 
         // 自由做市模式
         if mode == "free".to_string() {
@@ -524,7 +524,7 @@ impl Strategy {
             sell_start * (Decimal::ONE + predict + open * sell_shift - avoid),            // sell lower
             sell_start * (Decimal::ONE + predict + open * sell_shift + avoid),            // sell upper
         ];
-        debug!(?avoid, ?buy_shift, ?sell_shift, ?avoid, ?open_dist);
+        // debug!(?avoid, ?buy_shift, ?sell_shift, ?avoid, ?open_dist);
 
 
         // let mut open_dist = vec![
@@ -545,15 +545,15 @@ impl Strategy {
         self.close_dist = close_dist.clone();
         info!(?open_dist);
         info!(?close_dist);
-        debug!(?open_dist);
-        debug!(?close_dist);
+        // debug!(?open_dist);
+        // debug!(?close_dist);
     }
 
     // 统计请求次数
     #[instrument(skip(self, command), level="TRACE")]
     pub fn _update_request_num(&mut self, command: &OrderCommand) {
-        debug!(?command);
-        debug!(?self.request_order_count, ?self.request_count);
+        // debug!(?command);
+        // debug!(?self.request_order_count, ?self.request_count);
 
         let order_count = (command.limits_open.len() + command.limits_close.len()).to_i64().unwrap();
         let request_count = order_count + (command.cancel.len() + command.check.len()).to_i64().unwrap();
@@ -561,7 +561,7 @@ impl Strategy {
         self.request_order_count += order_count;
         self.request_count += request_count;
 
-        debug!(?self.request_order_count, ?self.request_count);
+        // debug!(?self.request_order_count, ?self.request_count);
     }
 
     // 根据平均请求次数限制开仓下单
@@ -620,9 +620,9 @@ impl Strategy {
             }
         }
 
-        debug!(?command);
+        // debug!(?command);
         command.cancel = new_cancel;
-        debug!(?command);
+        // debug!(?command);
 
         // 释放撤单限制
         self._release_in_cancel();
@@ -631,7 +631,7 @@ impl Strategy {
     // 维护查单队列,检查是否在撤单
     #[instrument(skip(self), level="TRACE")]
     pub fn _release_in_check(&mut self) {
-        debug!(?self.in_check);
+        // debug!(?self.in_check);
         // 为什么要移出来:Rust不允许边循环边修改map
         let mut to_remove = Vec::new();
 
@@ -643,7 +643,7 @@ impl Strategy {
             }
 
             // 等待超时,就移除正在撤单队列
-            debug!("移除查单队列:{}", client_id.clone());
+            // debug!("移除查单队列:{}", client_id.clone());
             to_remove.push(client_id.clone());
         }
 
@@ -651,13 +651,13 @@ impl Strategy {
         for client_id in to_remove {
             self.in_check.remove(&client_id);
         }
-        debug!(?self.in_check);
+        // debug!(?self.in_check);
     }
 
     // 检查是否正在撤单
     #[instrument(skip(self), level="TRACE")]
     pub fn _release_in_cancel(&mut self) {
-        debug!(?self.in_cancel);
+        // debug!(?self.in_cancel);
         // 为什么要移出来:Rust不允许边循环边修改map
         let mut to_remove = Vec::new();
 
@@ -669,7 +669,7 @@ impl Strategy {
             }
 
             // 等待超时,就移除正在撤单队列
-            debug!("等待超过后移除正在撤单队列:{}", client_id.clone());
+            // debug!("等待超过后移除正在撤单队列:{}", client_id.clone());
             to_remove.push(client_id.clone());
         }
 
@@ -677,7 +677,7 @@ impl Strategy {
         for client_id in to_remove {
             self.in_cancel.remove(&client_id);
         }
-        debug!(?self.in_cancel);
+        // debug!(?self.in_cancel);
     }
 
     // 刷新请求限制
@@ -695,16 +695,16 @@ impl Strategy {
     // 刷新持仓比例
     #[instrument(skip(self), level="TRACE")]
     pub fn _pos_rate(&mut self) {
-        debug!(?self);
+        // debug!(?self);
 
         if self.max_long_value > Decimal::ZERO {
             self.long_hold_rate = self.long_hold_value / self.max_long_value;
-            debug!(?self.long_hold_rate);
+            // debug!(?self.long_hold_rate);
         }
 
         if self.max_short_value > Decimal::ZERO {
             self.short_hold_rate = self.short_hold_value / self.max_short_value;
-            debug!(?self.short_hold_rate);
+            // debug!(?self.short_hold_rate);
         }
     }
 
@@ -726,7 +726,7 @@ impl Strategy {
             // 统计请求频率
             self._update_request_num(&mut command);
         }
-        debug!(?command);
+        // debug!(?command);
 
         return command;
     }
@@ -749,7 +749,7 @@ impl Strategy {
             // 统计请求频率
             self._update_request_num(&mut command);
         }
-        debug!(?command);
+        // debug!(?command);
 
         return command;
     }
@@ -760,7 +760,7 @@ impl Strategy {
         // 撤掉全部挂单
         let mut pd_amount = Decimal::ZERO;
         let mut pk_amount = Decimal::ZERO;
-        debug!(?self.local_orders);
+        // debug!(?self.local_orders);
         for client_id in self.local_orders.keys() {
             let order = self.local_orders.get(client_id).unwrap();
 
@@ -776,12 +776,12 @@ impl Strategy {
                 pd_amount += order.amount;
             }
         }
-        debug!(?pd_amount, ?pk_amount);
+        // debug!(?pd_amount, ?pk_amount);
 
         // 批量挂单
         let need_close_long = self.pos.long_pos - pd_amount;
         let need_close_short = self.pos.short_pos - pk_amount;
-        debug!(?need_close_long, ?need_close_short);
+        // debug!(?need_close_long, ?need_close_short);
 
         // 做多仓位平仓
         if need_close_long * self.mp > self._min_amount_value {
@@ -801,7 +801,7 @@ impl Strategy {
             ];
             command.limits_close.insert(client_id.clone(), value);
 
-            debug!(?self.pos.long_pos, ?self.mp, ?need_close_long, ?command)
+            // debug!(?self.pos.long_pos, ?self.mp, ?need_close_long, ?command)
         }
 
         // 做空仓位平仓
@@ -821,7 +821,7 @@ impl Strategy {
             ];
             command.limits_close.insert(client_id.clone(), value);
 
-            debug!(?self.pos.short_pos, ?self.mp, ?need_close_short, ?command)
+            // debug!(?self.pos.short_pos, ?self.mp, ?need_close_short, ?command)
         }
     }
 
@@ -834,7 +834,7 @@ impl Strategy {
         let pre_hot:i64 = 10 * 1000;
         if !self.mp.eq(&Decimal::ZERO) && self.local_time - self.local_start_time > pre_hot {
             self.is_ready = true;
-            debug!(?self.mp, ?self.local_time, ?self.local_start_time, ?pre_hot);
+            // debug!(?self.mp, ?self.local_time, ?self.local_start_time, ?pre_hot);
             info!("策略预热完毕,可以执行后续逻辑!")
         }
 
@@ -852,7 +852,7 @@ impl Strategy {
     // 平仓订单处理命令
     #[instrument(skip(self, command), level="TRACE")]
     pub fn _post_close(&self, command: &mut OrderCommand) {
-        debug!(?command);
+        // debug!(?command);
 
         let mut pd_amount = Decimal::ZERO;
         let mut pd_order_num = 0;
@@ -888,7 +888,7 @@ impl Strategy {
                 pd_order_num += 1;
             }
         }
-        debug!(?command);
+        // debug!(?command);
 
         // 判断是否需要全平
         let is_need_cancel_all_close =
@@ -906,7 +906,7 @@ impl Strategy {
                 }
             }
         }
-        debug!(?command);
+        // debug!(?command);
 
         // 区分现货和期货
         if self.exchange.contains("spot") {
@@ -929,7 +929,7 @@ impl Strategy {
                         ];
                         command.limits_close.insert(order_client_id, order.clone());
 
-                        debug!(?command);
+                        // debug!(?command);
                     }
                 }
             }
@@ -953,7 +953,7 @@ impl Strategy {
                         ];
                         command.limits_close.insert(order_client_id, order.clone());
 
-                        debug!(?command);
+                        // debug!(?command);
                     }
                 }
             }
@@ -987,7 +987,7 @@ impl Strategy {
 
                     command.limits_close.insert(order_client_limit_id, order_limit.clone());
                     info!(?command);
-                    debug!(?command);
+                    // debug!(?command);
                 }
             }
 
@@ -1021,7 +1021,7 @@ impl Strategy {
 
                     command.limits_close.insert(order_client_limit_id, order_limit.clone());
                     info!(?command);
-                    debug!(?command);
+                    // debug!(?command);
                 }
             }
         }
@@ -1030,7 +1030,7 @@ impl Strategy {
     // 生成取消订单的指令
     #[instrument(skip(self, command), level="TRACE")]
     pub fn _cancel_open(&self, command: &mut OrderCommand) {
-        debug!(?command);
+        // debug!(?command);
         // 挂单范围
         let long_upper = self.open_dist[0];
         let long_lower = self.open_dist[1];
@@ -1048,7 +1048,7 @@ impl Strategy {
                 if order.price <= long_upper && order.price >= long_lower {
                     continue
                 }
-                debug!(?key, ?order.price, ?long_upper, ?long_lower);
+                // debug!(?key, ?order.price, ?long_upper, ?long_lower);
                 command.cancel.insert(key.clone(), value.clone());
             }
 
@@ -1058,7 +1058,7 @@ impl Strategy {
                 if order.price >= short_lower && order.price <= short_upper {
                     continue
                 }
-                debug!(?key, ?order.price, ?short_lower, ?short_upper);
+                // debug!(?key, ?order.price, ?short_lower, ?short_upper);
                 command.cancel.insert(key.clone(), value.clone());
             }
         }
@@ -1067,7 +1067,7 @@ impl Strategy {
     // 超时触发查单信号
     #[instrument(skip(self, command), level="TRACE")]
     pub fn _check_local_orders(&mut self, command: &mut OrderCommand) {
-        debug!(?command);
+        // debug!(?command);
         // 超时检测
         if self.local_time - self._check_local_orders_time < self._check_local_orders_interval {
             return;
@@ -1095,9 +1095,9 @@ impl Strategy {
             ];
             command.check.insert(key, value);
             self.in_check.insert(client_id.clone(), self.local_time);
-            debug!("查询订单:{:?}", client_id.clone());
+            // debug!("查询订单:{:?}", client_id.clone());
 
-            debug!(?command);
+            // debug!(?command);
         }
 
         // 维护查单队列
@@ -1109,7 +1109,7 @@ impl Strategy {
     // 开单指令生成逻辑
     #[instrument(skip(self, command), level="TRACE")]
     pub fn _post_open(&mut self, command: &mut OrderCommand) {
-        debug!(?command);
+        // debug!(?command);
         // 开仓逻辑检测,主要是检测整点开仓逻辑
         if !self.check_allow_post_open() {
             return;
@@ -1120,6 +1120,9 @@ impl Strategy {
             return;
         }
 
+        // 报单时间更新
+        self.post_open_time = self.local_time;
+
         // 挂单范围获取
         let long_upper = self.open_dist[0];
         let long_lower = self.open_dist[1];
@@ -1147,7 +1150,7 @@ impl Strategy {
         // 计算可开价值
         let mut long_free_value = self.max_long_value - self.long_hold_value - buy_value;
         let mut short_free_value = self.max_short_value - self.short_hold_value - sell_value;
-        debug!(?long_free_value, ?short_free_value);
+        // debug!(?long_free_value, ?short_free_value);
         // 现货要特殊处理
         if self.exchange.contains("spot") {
             let coin_value = self.coin * self.mp * self.lever_rate * self.adjust_lever_rate;
@@ -1160,10 +1163,10 @@ impl Strategy {
         let one_hand_long_value = dec!(0.99) * (self.max_long_value / self.grid);
         let one_hand_short_value = dec!(0.99) * (self.max_short_value / self.grid);
 
-        debug!(?self.post_side);
+        // debug!(?self.post_side);
         // 挂多单
         if self.post_side >= 0 {
-            debug!(?buy_price_list);
+            // debug!(?buy_price_list);
             if buy_price_list.len() == 0 {
                 let mut target_buy_price = (long_upper + long_lower) * dec!(0.5);
                 target_buy_price = utils::clip(target_buy_price, self.bp * dec!(0.97), self.ap * dec!(1.0005));
@@ -1171,7 +1174,7 @@ impl Strategy {
                 let value = min(one_hand_long_value, long_free_value);
                 let amount = utils::fix_amount(value / self.mp, self.step_size);
                 let amount_value = amount * self.mp;
-                debug!(?one_hand_long_value, ?long_free_value, ?amount);
+                // debug!(?one_hand_long_value, ?long_free_value, ?amount);
 
                 // 下单价值不能太大,也不能太小
                 if amount_value >= self._min_amount_value && amount_value <= long_free_value {
@@ -1183,17 +1186,14 @@ impl Strategy {
                         client_id.clone(),
                     ];
 
-                    debug!(?order);
+                    // debug!(?order);
                     command.limits_open.insert(client_id.clone(), order);
-
-                    // 报单时间更新
-                    self.post_open_time = self.local_time;
                 }
             }
         }
         // 挂空单
         if self.post_side <= 0 {
-            debug!(?sell_price_list);
+            // debug!(?sell_price_list);
             if sell_price_list.len() == 0 {
                 let mut target_sell_price = (short_lower + short_upper) * dec!(0.5);
                 target_sell_price = utils::clip(target_sell_price, self.bp * dec!(0.9995), self.ap * dec!(1.03));
@@ -1212,11 +1212,8 @@ impl Strategy {
                         client_id.clone(),
                     ];
 
-                    debug!(?order);
+                    // debug!(?order);
                     command.limits_open.insert(client_id.clone(), order);
-
-                    // 报单时间更新
-                    self.post_open_time = self.local_time;
                 }
             }
         }
@@ -1279,7 +1276,6 @@ impl Strategy {
 mod tests {
     use rust_decimal::Decimal;
     use rust_decimal_macros::dec;
-    use tracing::{debug};
     use crate::model::{OrderInfo, TraderMsg};
     use global::params::Params;
     use crate::strategy::Strategy;
@@ -1316,6 +1312,6 @@ mod tests {
         strategy.equity = dec!(1000);
         strategy.lever_rate = Decimal::ONE;
 
-        debug!("{:?}", strategy.on_time(&trader_msg));
+        // debug!("{:?}", strategy.on_time(&trader_msg));
     }
 }

+ 22 - 0
test_account.toml.sample

@@ -0,0 +1,22 @@
+# gate账号密码
+gate_access_key = ""
+gate_secret_key = ""
+
+# binance账号密码
+binance_access_key = ""
+binance_secret_key = ""
+
+# kucoin账号密码
+kucoin_access_key = ""
+kucoin_secret_key = ""
+kucoin_pass = ""
+
+# okx账号密码
+okx_access_key = ""
+okx_secret_key = ""
+okx_pass = ""
+
+# bitget账号密码
+bitget_access_key = ""
+bitget_secret_key = ""
+bitget_pass = ""