Pārlūkot izejas kodu

Merge branch 'dev' into exchange_data_formatter

gepangpang 2 gadi atpakaļ
vecāks
revīzija
8109522624
67 mainītis faili ar 6896 papildinājumiem un 909 dzēšanām
  1. 4 2
      .gitignore
  2. 1 0
      Cargo.toml
  3. 21 7
      config.toml.sample
  4. 11 5
      exchanges/src/binance_spot_ws.rs
  5. 22 3
      exchanges/src/binance_swap_rest.rs
  6. 10 6
      exchanges/src/binance_swap_ws.rs
  7. 791 0
      exchanges/src/bitget_spot_rest.rs
  8. 507 0
      exchanges/src/bitget_spot_ws.rs
  9. 37 8
      exchanges/src/gate_swap_rest.rs
  10. 10 6
      exchanges/src/gate_swap_ws.rs
  11. 0 18
      exchanges/src/http_tool.rs
  12. 64 5
      exchanges/src/kucoin_swap_rest.rs
  13. 11 8
      exchanges/src/kucoin_swap_ws.rs
  14. 3 0
      exchanges/src/lib.rs
  15. 207 13
      exchanges/src/okx_swap_rest.rs
  16. 144 44
      exchanges/src/okx_swap_ws.rs
  17. 8 3
      exchanges/src/response_base.rs
  18. 8 0
      exchanges/src/utils.rs
  19. 1 0
      exchanges/tests/binance_spot_test.rs
  20. 15 7
      exchanges/tests/binance_swap_test.rs
  21. 410 0
      exchanges/tests/bitget_spot_test.rs
  22. 53 25
      exchanges/tests/gate_swap_test.rs
  23. 43 1
      exchanges/tests/kucoin_swap_test.rs
  24. 224 0
      exchanges/tests/okx_swap_test.rs
  25. 45 44
      exchanges/tests/test.rs
  26. 1 0
      global/.gitignore
  27. 8 3
      global/Cargo.toml
  28. 1 0
      global/src/lib.rs
  29. 126 17
      global/src/log_utils.rs
  30. 4 7
      global/src/params.rs
  31. 2 1
      global/src/public_params.rs
  32. 118 0
      global/src/trace_stack.rs
  33. 33 0
      global/tests/appender_test.rs
  34. 1 3
      src/control_c.rs
  35. 45 14
      src/main.rs
  36. 18 7
      src/quant_libs.rs
  37. 9 15
      src/server.rs
  38. 1 0
      standard/.gitignore
  39. 48 34
      standard/src/binance_spot.rs
  40. 136 36
      standard/src/binance_swap.rs
  41. 576 0
      standard/src/bitget_spot.rs
  42. 119 0
      standard/src/bitget_spot_handle.rs
  43. 15 4
      standard/src/exchange.rs
  44. 53 32
      standard/src/gate_handle.rs
  45. 48 37
      standard/src/gate_spot.rs
  46. 260 57
      standard/src/gate_swap.rs
  47. 61 18
      standard/src/handle_info.rs
  48. 38 29
      standard/src/kucoin_handle.rs
  49. 253 67
      standard/src/kucoin_swap.rs
  50. 38 3
      standard/src/lib.rs
  51. 158 0
      standard/src/okx_handle.rs
  52. 576 0
      standard/src/okx_swap.rs
  53. 31 0
      standard/tests/binance_spot_handle_test.rs
  54. 195 0
      standard/tests/bitget_spot_test.rs
  55. 186 31
      standard/tests/exchange_test.rs
  56. 1 1
      standard/tests/gate_swap_test.rs
  57. 13 1
      standard/tests/kucoin_swap_test.rs
  58. 68 0
      standard/tests/okx_handle_test.rs
  59. 195 0
      standard/tests/okx_swap_test.rs
  60. 313 55
      strategy/src/exchange_disguise.rs
  61. 0 0
      strategy/src/gp_predictor.rs
  62. 2 1
      strategy/src/lib.rs
  63. 14 7
      strategy/src/model.rs
  64. 336 173
      strategy/src/quant.rs
  65. 121 40
      strategy/src/strategy.rs
  66. 13 10
      strategy/src/utils.rs
  67. 12 1
      strategy/tests/decimal_test.rs

+ 4 - 2
.gitignore

@@ -2,5 +2,7 @@
 /.idea
 
 Cargo.lock
-config.toml
-*.log
+config.toml*
+*.log
+*.log.*
+/logs*

+ 1 - 0
Cargo.toml

@@ -14,6 +14,7 @@ tokio = { version = "1.31.0", features = ["full"] }
 chrono = "0.4.26"
 tracing = "0.1"
 tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+tracing-appender = "0.2.2"
 serde = { version = "1.0.188", features = ["derive"] }
 actix-rt = "2.5.0"
 actix-web = "4.0.0-beta.12"

+ 21 - 7
config.toml.sample

@@ -1,28 +1,42 @@
+# 经纪商名,用于生成本地订单id
 broker_id = "gate"
+# 交易账户名
 account_name = "gate_account"
+# 交易账户api_key
 access_key = ""
+# 交易账户access_key
 secret_key = ""
+# 交易账户pass_key(部分交易所不需要填写)
 pass_key = ""
+# 交易交易所,可选[gate_usdt_swap, kucoin_usdt_swap]
 exchange = "gate_usdt_swap"
+# 交易交易所的交易对
 pair = "blz_usdt"
+# 开仓距离,0.01代表1%
 open = 0.001
+# 平仓距离,0.01代表1%
 close = 0.0002
+# 杠杆倍率
 lever_rate = 0.1
-# 延迟时间,改成ms级别
+# 现货底仓
+hold_coin = 0
+# quant的run_strategy函数,用于定期检查使用,单位是毫秒
 interval = 100
+# 参考交易所,可选[gate_usdt_swap, kucoin_usdt_swap, binance_usdt_swap, binance_spot]
 ref_exchange = ["binance_usdt_swap"]
+# 参考交易对
 ref_pair = ["blz_usdt"]
+# 资金使用比率,0.9代表90%
 used_pct = 0.9
-index = 0
-save = 0
-hold_coin = 0.0
-log = 1
+# 总体止损比例,0.02代表2%
 stop_loss = 0.02
+# gamma值,参与预测价格计算的
 gamma = 0.999
+# 分批建仓功能 小资金建议1 大资金建议3 默认 1
 grid = 1
-place_order_limit = 0
+# 是否启用colo通道
 colo = 0
-# 日志级别,从低到高依次是:[trace, debug, info, warn, error]
+# 日志级别,从低到高依次是:[trace, debug, info, warn, error],推荐使用info
 log_level = "info"
 # 中控端口
 port = 6000

+ 11 - 5
exchanges/src/binance_spot_ws.rs

@@ -12,6 +12,7 @@ use tungstenite::protocol::WebSocketConfig;
 use url::Url;
 use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::utils::get_time_microsecond;
 
 pub enum BinanceSpotWsType {
     //订阅频道类型
@@ -62,9 +63,7 @@ impl BinanceSpotWs {
                      sender: Sender<ResponseData>,
     ) -> BinanceSpotWs
     {
-        if is_colo {
-            trace!("不支持高速通道")
-        }
+
 
         /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
         let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
@@ -76,6 +75,11 @@ impl BinanceSpotWs {
             }
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",request_url);
+        }else{
+            info!("走普通通道:{}",request_url);
+        }
         /*****返回结构体*******/
         BinanceSpotWs {
             label,
@@ -235,7 +239,8 @@ impl BinanceSpotWs {
             match msg {
                 Ok(Message::Text(text)) => {
                     // trace!("获取推送:{}",text.clone());
-                    let res_data = Self::ok_text(lable.to_string(), text);
+                    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 {
@@ -291,7 +296,8 @@ impl BinanceSpotWs {
             match msg {
                 Ok(Message::Text(text)) => {
                     // trace!("获取推送:{}",text.clone());
-                    let res_data = Self::ok_text(lable.to_string(), text);
+                    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 {

+ 22 - 3
exchanges/src/binance_swap_rest.rs

@@ -7,7 +7,7 @@ use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
 use crate::http_tool::RestTool;
 use crate::response_base::ResponseData;
-use tracing::trace;
+use tracing::{info, trace};
 use ring::hmac;
 use serde_json::json;
 
@@ -35,12 +35,16 @@ impl BinanceSwapRest {
     pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BinanceSwapRest
     {
         let base_url = if is_colo {
-            trace!("不支持colo高速线路");
             "https://fapi.binance.com".to_string()
         } else {
             "https://fapi.binance.com".to_string()
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
         /*****返回结构体*******/
         BinanceSwapRest {
             label,
@@ -228,6 +232,21 @@ impl BinanceSwapRest {
         ).await;
         data
     }
+    //根据币对 撤销全部订单
+    pub async fn cancel_order_all(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "recvWindow":"2000",
+         });
+        let data = self.request("DELETE".to_string(),
+                                "".to_string(),
+                                format!("/fapi/v1/allOpenOrders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
 
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
@@ -392,7 +411,7 @@ impl BinanceSwapRest {
     //res_data 解析
     pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, params: String) -> ResponseData {
         match result {
-            Ok(res_data) => { 
+            Ok(res_data) => {
                 if res_data.code != "200" {
                     let message = res_data.message;
                     let json_value: serde_json::Value = serde_json::from_str(&message).unwrap();

+ 10 - 6
exchanges/src/binance_swap_ws.rs

@@ -12,6 +12,7 @@ use tungstenite::protocol::WebSocketConfig;
 use url::Url;
 use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::utils::get_time_microsecond;
 
 pub enum BinanceWsType {
     //订阅频道类型
@@ -61,10 +62,6 @@ impl BinanceSwapWs {
                      sender: Sender<ResponseData>,
     ) -> BinanceSwapWs
     {
-        if is_colo {
-            trace!("不支持高速通道")
-        }
-
         /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
         let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
 
@@ -75,6 +72,11 @@ impl BinanceSwapWs {
             }
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",request_url);
+        } else {
+            info!("走普通通道:{}",request_url);
+        }
         /*****返回结构体*******/
         BinanceSwapWs {
             label,
@@ -229,7 +231,8 @@ impl BinanceSwapWs {
             match msg {
                 Ok(Message::Text(text)) => {
                     // trace!("获取推送:{}",text.clone());
-                    let res_data = Self::ok_text(lable.to_string(), text);
+                    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 {
@@ -281,7 +284,8 @@ impl BinanceSwapWs {
             match msg {
                 Ok(Message::Text(text)) => {
                     // trace!("获取推送:{}",text.clone());
-                    let res_data = Self::ok_text(lable.to_string(), text);
+                    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 {

+ 791 - 0
exchanges/src/bitget_spot_rest.rs

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

+ 507 - 0
exchanges/src/bitget_spot_ws.rs

@@ -0,0 +1,507 @@
+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::time::Duration;
+use chrono::Utc;
+use tokio::sync::mpsc::Sender;
+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 crate::response_base::ResponseData;
+use crate::utils::get_time_microsecond;
+
+use ring::hmac;
+
+pub enum BitgetWsType {
+    //频道类型
+    Public,
+    Private,
+}
+
+
+#[derive(Clone)]                        //订阅枚举
+pub enum BitgetSubscribeType {
+    PuTicker,
+    PuCandle1m,
+    PuTrade,
+    PuBooks5,
+
+    PrAccount,
+    PrOrders,
+}
+
+#[derive(Clone)]
+pub struct BitgetSpotWs {
+    pub label: String,
+    request_url: String,
+    //实际ws 链接地址
+    proxy: ParsingDetail,
+    //账号信息
+    login_param: BTreeMap<String, String>,
+    //kuconis特殊参数
+    symbol_s: Vec<String>,
+    //订阅币对
+    subscribe_types: Vec<BitgetSubscribeType>,
+    //订阅信息
+    sender: Sender<ResponseData>,     //数据通道
+}
+
+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_label(label: String, is_colo: bool,
+                     login_param: BTreeMap<String, String>,
+                     ws_type: BitgetWsType,
+                     sender: Sender<ResponseData>,
+    ) -> BitgetSpotWs
+    {
+        /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
+        let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
+
+
+        let request_url = match ws_type {
+            BitgetWsType::Public => {
+                format!("wss://ws.bitget.com/v2/ws/public")
+            }
+            BitgetWsType::Private => {
+                format!("wss://ws.bitget.com/v2/ws/private")
+            }
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",request_url);
+        } else {
+            info!("走普通通道:{}",request_url);
+        }
+        /*****返回结构体*******/
+        BitgetSpotWs {
+            label,
+            request_url,
+            proxy: parsing_detail,
+            login_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            sender,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<BitgetSubscribeType>) {
+        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() {
+            // 大写
+            *symbol = symbol.to_uppercase();
+            // 字符串替换
+            *symbol = symbol.replace("-", "");
+            *symbol = symbol.replace("_", "");
+        }
+        self.symbol_s = symbol_s;
+        self.run(bool_v1).await;
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: BitgetSubscribeType) -> Value {
+        match subscribe_type {
+            BitgetSubscribeType::PuTicker => {
+                json!({
+                    "instType": "SPOT",
+                    "channel": "ticker",
+                    "instId": symbol,
+                })
+            }
+            BitgetSubscribeType::PuCandle1m => {
+                json!({
+                    "instType": "SPOT",
+                    "channel": "candle1m",
+                    "instId": symbol,
+                })
+            }
+            BitgetSubscribeType::PuTrade => {
+                json!({
+                    "instType": "SPOT",
+                    "channel": "trade",
+                    "instId": symbol,
+                })
+            }
+            BitgetSubscribeType::PuBooks5 => {
+                json!({
+                    "instType": "SPOT",
+                    "channel": "books5",
+                    "instId": symbol,
+                })
+            }
+            BitgetSubscribeType::PrAccount => {
+                json!({
+                    "instType": "SPOT",
+                    "channel": "account",
+                    "coin": "default"
+                })
+            }
+            BitgetSubscribeType::PrOrders => {
+                json!({
+                    "instType": "SPOT",
+                    "channel": "orders",
+                    "instId": symbol
+                })
+            }
+        }
+    }
+    //组装订阅数据
+    pub fn get_subscription(&self) -> String {
+        let mut args = vec![];
+        for symbol in &self.symbol_s {
+            for subscribe_type in &self.subscribe_types {
+                let ty_str = Self::enum_to_string(symbol.clone(), subscribe_type.clone());
+                args.push(ty_str);
+            }
+        }
+        let str = json!({
+            "op": "subscribe",
+            "args": args
+        });
+
+        trace!("订阅信息:{}", str.to_string());
+
+        str.to_string()
+    }
+
+    // fn 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();
+            }
+        }
+        if access_key.len() > 0 || secret_key.len() > 0 || passphrase.len() > 0 {
+            let timestamp = Utc::now().timestamp().to_string();
+            // 时间戳 + 请求类型+ 请求参数字符串
+            let message = format!("{}GET{}", timestamp, "/user/verify");
+            let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+            let result = ring::hmac::sign(&hmac_key, &message.as_bytes());
+            let sign = base64::encode(result);
+
+            let login_json = json!({
+                              "op": "login",
+                              "args": [{
+                                "apiKey": access_key,
+                                "passphrase": passphrase,
+                                "timestamp": timestamp,
+                                "sign": sign
+                              }]
+                        });
+
+            trace!("---login_json:{0}", login_json.to_string());
+            trace!("--登陆:{}", login_json.to_string());
+            login_json_str = login_json.to_string();
+        }
+        login_json_str
+    }
+    /*******************************************************************************************************/
+    /*****************************************socket基本*****************************************************/
+    /*******************************************************************************************************/
+    async fn run(&mut self, bool_v1: Arc<AtomicBool>)
+    {
+        //订阅信息组装
+        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: 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));
+        }
+        /*****消息溜***/
+        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(label.to_string(), text);
+                    res_data.time = get_time_microsecond();
+                    if res_data.code == "-200" {
+                        trace!("订阅成功:{:?}", res_data.data);
+                    } else if res_data.code == "-201" {
+                        trace!("登陆:{:?}", res_data);
+                        /*****订阅***/
+                        if subscription.len() > 0 {
+                            // trace!("订阅信息:{}", subscription);
+                            web_socket.write_message(Message::Text(subscription.clone()))
+                                .unwrap();
+                        }
+                    } 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();
+    }
+
+    //非代理
+    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
+                          subscription: String)
+    {
+        info!("链接成功!开始数据读取");
+        let label = self.label.clone();
+        /*****订阅***/
+        if subscription.len() > 0 {
+            trace!("订阅信息:{}",subscription);
+            web_socket.write_message(Message::Text(subscription))
+                .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();
+    }
+
+    //数据解析
+    pub fn ok_text(label: String, text: String) -> ResponseData
+    {
+        // trace!("原始数据:{}", text);
+        let mut res_data = ResponseData::new(label.to_string(), "200".to_string(), "success".to_string(), "".to_string());
+        let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
+
+        // {"event":"login","code":0}
+        if json_value.get("event").is_some() && json_value["event"].as_str() == Option::from("login") {
+            if json_value.get("code").is_some() && json_value["code"] == 0 {
+                res_data.message = format!("登陆成功");
+            } else {
+                res_data.message = format!("登陆失败:({},{})", json_value.get("code").as_ref().unwrap(), json_value.get("msg").as_ref().unwrap());
+            }
+            res_data.channel = format!("login");
+            res_data.code = "-201".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.data = text;
+            res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+            res_data
+        } else if json_value.get("action").is_some() {
+            // trace!("解析-推送数据:{}", text);
+            res_data.code = "200".to_string();
+            res_data.data = json_value["data"].to_string();
+            res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
+            res_data
+        } else {
+            res_data
+        }
+
+        // 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.clone().to_string();
+        // } else if json_value["result"]["status"].as_str() == Option::from("success") {//订阅返回
+        //     res_data.code = "-200".to_string();
+        //     res_data.data = text;
+        // } else {
+        //     res_data.channel = format!("{}", json_value["channel"].as_str().unwrap());
+        //     res_data.code = "200".to_string();
+        //     res_data.data = json_value["result"].to_string();
+        // }
+    }
+}

+ 37 - 8
exchanges/src/gate_swap_rest.rs

@@ -7,10 +7,11 @@ use reqwest::Client;
 use rust_decimal::Decimal;
 use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
+use serde_json::Value;
 use crate::http_tool::RestTool;
 use crate::response_base::ResponseData;
 use sha2::Sha512;
-use tracing::trace;
+use tracing::{error, info, trace};
 
 #[derive(Clone)]
 pub struct GateSwapRest {
@@ -36,12 +37,17 @@ impl GateSwapRest {
     pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> GateSwapRest
     {
         let base_url = if is_colo {
-            trace!("使用colo高速线路");
-            "https://apiv4-private.gateapi.io".to_string()
+            // "https://apiv4-private.gateapi.io".to_string()
+            "https://api.gateio.ws".to_string()
         } else {
             "https://api.gateio.ws".to_string()
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
         /*****返回结构体*******/
         GateSwapRest {
             label,
@@ -281,6 +287,19 @@ impl GateSwapRest {
         ).await;
         data
     }
+    //撤销所有挂单
+    pub async fn cancel_order_all(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                format!("/sprd/mass-cancel"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
     //弃用
     pub async fn swap_bazaar_order(&mut self, text: String, origin_side: String, settle: String, contract: String, size: i64) -> ResponseData {
         let mut reduce_only = false;
@@ -550,11 +569,21 @@ impl GateSwapRest {
             Ok(res_data) => {
                 if res_data.code != "200" {
                     let message = res_data.message;
-                    let json_value: serde_json::Value = serde_json::from_str(&message).unwrap();
-                    let label = json_value["label"].as_str().unwrap();
-                    let mut error = ResponseData::error(res_data.label, label.parse().unwrap());
-                    error.data = format!("请求地址:{},请求参数:{}", base_url, params);
-                    error
+                    // let json_value: serde_json::Value = serde_json::from_str(&message).unwrap();//这种方式会触发 解析错误
+                    let json_value = serde_json::from_str::<Value>(&message);
+                    match json_value {
+                        Ok(data) => {
+                            let label = data["label"].as_str().unwrap();
+                            let mut error = ResponseData::error(res_data.label, label.parse().unwrap());
+                            error.data = format!("请求地址:{},请求参数:{}", base_url, params);
+                            error
+                        }
+                        Err(e) => {
+                            error!("解析错误:{:?}",e);
+                            let error = ResponseData::error("".to_string(), format!("json 解析失败:{},相关参数:{}", e, message));
+                            error
+                        }
+                    }
                 } else {
                     res_data
                 }

+ 10 - 6
exchanges/src/gate_swap_ws.rs

@@ -16,6 +16,7 @@ use tungstenite::protocol::WebSocketConfig;
 use url::Url;
 use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::utils::get_time_microsecond;
 
 pub enum GateWsType {
     //频道类型
@@ -69,9 +70,6 @@ impl GateSwapWs {
                      sender: Sender<ResponseData>,
     ) -> GateSwapWs
     {
-        if is_colo {
-            trace!("支持高速通道-未配置")
-        } else {}
 
         /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
         let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
@@ -83,7 +81,11 @@ impl GateSwapWs {
             }
         };
 
-
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",request_url);
+        } else {
+            info!("走普通通道:{}",request_url);
+        }
         /*****返回结构体*******/
         GateSwapWs {
             label,
@@ -311,7 +313,8 @@ impl GateSwapWs {
             match msg {
                 Ok(Message::Text(text)) => {
                     // trace!("获取推送:{}",text.clone());
-                    let res_data = Self::ok_text(label.to_string(), text);
+                    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 {
@@ -386,7 +389,8 @@ impl GateSwapWs {
             match msg {
                 Ok(Message::Text(text)) => {
                     // trace!("获取推送:{}",text.clone());
-                    let res_data = Self::ok_text(label.to_string(), text);
+                    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 {

+ 0 - 18
exchanges/src/http_tool.rs

@@ -12,24 +12,6 @@ impl RestTool {
     pub fn new(base_url: String) -> RestTool {
         RestTool { base_url, client: Client::new() }
     }
-    pub async fn demo() -> Result<(), reqwest::Error> {
-        let rest = RestTool::new("https://www.okx.com".to_string());
-        let params = serde_json::json!({
-            "instType":"SWAP",
-         });
-
-        let get_response = rest.http_toll(
-            "/api/v5/public/instruments".to_string(),
-            "get".to_string(),
-            params.to_string(),
-            HeaderMap::new(),
-        ).await;
-        let res_data = RestTool::res_data_analysis(get_response);
-        trace!("GET Response: {:?}", res_data);
-
-
-        Ok(())
-    }
     pub async fn http_toll(&self, request_path: String, request_type: String, params: String, headers: HeaderMap) -> Result<ResponseData, reqwest::Error> {
         let res_data: ResponseData;
         /****请求接口与 地址*/

+ 64 - 5
exchanges/src/kucoin_swap_rest.rs

@@ -6,7 +6,7 @@ use rust_decimal::Decimal;
 use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
 use sha2::Sha256;
-use tracing::trace;
+use tracing::{info, trace};
 use crate::http_tool::RestTool;
 use crate::response_base::ResponseData;
 
@@ -36,12 +36,16 @@ impl KucoinSwapRest {
     }
     pub fn new_lable(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> KucoinSwapRest {
         let base_url = if is_colo {
-            trace!("不支持colo高速线路");
             "https://api-futures.kucoin.com".to_string()
         } else {
             "https://api-futures.kucoin.com".to_string()
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
         /*****返回结构体*******/
         KucoinSwapRest {
             label,
@@ -122,8 +126,11 @@ impl KucoinSwapRest {
     //查看订单列表
     pub async fn get_orders(&mut self, status: String, symbol: String) -> ResponseData {
         let mut params = serde_json::json!({
-            "symbol":symbol
+            // "symbol":symbol
          });
+        if symbol.len() > 0 {
+            params.as_object_mut().unwrap().insert("symbol".parse().unwrap(), serde_json::Value::from(symbol));
+        }
         if status.len() > 0 {
             params.as_object_mut().unwrap().insert("status".parse().unwrap(), serde_json::Value::from(status));
         }
@@ -243,9 +250,9 @@ impl KucoinSwapRest {
         ).await;
         data
     }
-    //批量测但
+    //批量撤单
     pub async fn cancel_orders(&mut self, symbol: String) -> ResponseData {
-        let  params = serde_json::json!({   });
+        let params = serde_json::json!({   });
         let data = self.request("DELETE".to_string(),
                                 "/api/v1".to_string(),
                                 format!("/orders?symbol={}", symbol),
@@ -254,6 +261,18 @@ impl KucoinSwapRest {
         ).await;
         data
     }
+    //全部撤單
+    pub async fn cancel_order_all(&mut self) -> ResponseData {
+        let params = serde_json::json!({   });
+        let data = self.request("DELETE".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/orders"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
 
     //获取合约令牌-公共
     pub async fn get_public_token(&mut self) -> ResponseData {
@@ -277,6 +296,46 @@ impl KucoinSwapRest {
         ).await;
         data
     }
+    //设置杠杆(修改階梯風險限額等級)
+    pub async fn set_leverage(&mut self, symbol: String, level: i8) -> ResponseData {
+        let params = serde_json::json!({
+            "symbol":symbol,
+            "level":level,
+        });
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/position/risk-limit-level/change"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查看杠杆(查詢杠桿代幣信息)
+    pub async fn get_leverage(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+        });
+        let data = self.request("GET".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/contracts/risk-limit/{}", symbol),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //设置 自动追加保证金
+    pub async fn auto_deposit_status(&mut self, symbol: String, status: bool) -> ResponseData {
+        let params = serde_json::json!({
+                "symbol":symbol,
+                "status":status
+        });
+        let data = self.request("POST".to_string(),
+                                "/api/v1".to_string(),
+                                format!("/position/margin/auto-deposit-status"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/

+ 11 - 8
exchanges/src/kucoin_swap_ws.rs

@@ -13,6 +13,7 @@ use url::Url;
 use crate::kucoin_swap_rest::KucoinSwapRest;
 use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::utils::get_time_microsecond;
 
 
 pub enum KucoinWsType {
@@ -70,16 +71,12 @@ impl KucoinSwapWs {
     ) -> KucoinSwapWs {
         return KucoinSwapWs::new_label("default-KucoinSwapWs".to_string(), is_colo, login_param, ws_type, sender).await;
     }
-    pub async fn new_label(label: String, is_colo: bool,
+    pub async fn new_label(label: String, _is_colo: bool,
                            login_param: BTreeMap<String, String>,
                            ws_type: KucoinWsType,
                            sender: Sender<ResponseData>,
     ) -> KucoinSwapWs
     {
-        if is_colo {
-            trace!("不支持高速通道")
-        }
-
         /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
         let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
 
@@ -99,6 +96,8 @@ impl KucoinSwapWs {
                 error!("-链接地址等参数错误:{:?}", error)
             }
         }
+
+
         /*****返回结构体*******/
         KucoinSwapWs {
             label,
@@ -162,7 +161,8 @@ impl KucoinSwapWs {
 
             Ok(KucoinWsParam { ws_url, token: ws_token, ws_ping_interval, ws_ping_timeout })
         } else {
-            panic!("公共/私有-频道获取失败:{:?}", res_data)
+            error!("公共/私有-频道获取失败:{:?}", res_data);
+            panic!("公共/私有-频道获取失败:{:?}", res_data);
         }
     }
     //自定义
@@ -170,6 +170,7 @@ impl KucoinSwapWs {
     {
         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;
     }
 
@@ -343,7 +344,8 @@ impl KucoinSwapWs {
 
                     trace!("获取推送:{}",text.clone());
                     // trace!(stdout, "Text-响应--{:?}", text.clone()).unwrap();
-                    let res_data = Self::ok_text(label.to_string(), text);
+                    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 {
@@ -426,7 +428,8 @@ impl KucoinSwapWs {
 
                     trace!("获取推送:{}",text.clone());
                     // trace!(stdout, "Text-响应--{:?}", text.clone()).unwrap();
-                    let res_data = Self::ok_text(label.to_string(), text);
+                    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 {

+ 3 - 0
exchanges/src/lib.rs

@@ -14,4 +14,7 @@ pub mod okx_swap_ws;
 pub mod binance_swap_ws;
 pub mod okx_swap_rest;
 pub mod binance_swap_rest;
+mod utils;
+pub mod bitget_spot_ws;
+pub mod bitget_spot_rest;
 

+ 207 - 13
exchanges/src/okx_swap_rest.rs

@@ -4,7 +4,7 @@ use reqwest::{Client};
 use rust_decimal::Decimal;
 use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
-use tracing::trace;
+use tracing::{info, trace};
 use crate::http_tool::RestTool;
 use crate::response_base::ResponseData;
 use ring::hmac;
@@ -35,12 +35,16 @@ impl OkxSwapRest {
     }
     pub fn new_lable(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> OkxSwapRest {
         let base_url = if is_colo {
-            trace!("不支持colo高速线路");
             "https://www.okx.com".to_string()
         } else {
             "https://www.okx.com".to_string()
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
         /*****返回结构体*******/
         OkxSwapRest {
             label,
@@ -56,8 +60,80 @@ impl OkxSwapRest {
     /*******************************************************************************************************/
     /*****************************************rest请求函数********************************************************/
     /*******************************************************************************************************/
+    //获取订单信息
+    pub async fn get_order(&mut self, symbol: String, ord_id: String, cl_ord_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "instId":symbol
+         });
+        if ord_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("ordId".parse().unwrap(), serde_json::Value::from(ord_id));
+        }
+        if cl_ord_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("clOrdId".parse().unwrap(), serde_json::Value::from(cl_ord_id));
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/trade/order".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取未成交订单列表
+    pub async fn get_incomplete_order(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "instId":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/trade/orders-pending".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取系统时间
+    pub async fn get_server_time(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/public/time".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查看持仓信息
+    pub async fn get_positions(&mut self, inst_type: String) -> ResponseData {
+        let mut params = serde_json::json!({
+         });
+        if inst_type.len() > 0 {
+            params.as_object_mut().unwrap().insert("instType".parse().unwrap(), serde_json::Value::from(inst_type));
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/account/positions".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取单个产品行情信息
+    pub async fn get_ticker(&mut self, symbol: String) -> ResponseData {
+        let params = serde_json::json!({
+            "instId":format!("{}-SWAP",symbol)
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/market/ticker".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
     //查看账户余额
-    pub async fn get_account(&mut self, ccy: String) -> ResponseData {
+    pub async fn get_balance(&mut self, ccy: String) -> ResponseData {
         let params = serde_json::json!({
             "ccy":ccy
          });
@@ -69,6 +145,96 @@ impl OkxSwapRest {
         ).await;
         data
     }
+    //获取交易产品基础信息
+    pub async fn get_instruments(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+           "instType":"SWAP"
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/public/instruments".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取成交明细(近三天)
+    pub async fn get_trade_fills(&mut self, after: String) -> ResponseData {
+        let mut params = serde_json::json!({
+             "instType": "SWAP",
+             "limit":"100"
+         });
+        if after.len() > 0 {
+            params.as_object_mut().unwrap().insert("after".parse().unwrap(), serde_json::Value::from(after));
+        }
+        let data = self.request("GET".to_string(),
+                                "/api/v5".to_string(),
+                                "/trade/fills".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    //合约-下单
+    pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                "/sprd/order".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //撤单
+    pub async fn cancel_order(&mut self, symbol: String, ord_id: String, cl_ord_id: String) -> ResponseData {
+        let mut params = serde_json::json!({
+             "instId": symbol
+         });
+        if ord_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("ordId".parse().unwrap(), serde_json::Value::from(ord_id));
+        }
+        if cl_ord_id.len() > 0 {
+            params.as_object_mut().unwrap().insert("clOrdId".parse().unwrap(), serde_json::Value::from(cl_ord_id));
+        }
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                "/trade/cancel-order".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //设置杠杆倍数-单币种保证金`账户在`全仓`交易模式下,设置`币币杠杆`的杠杆倍数(币对层面)
+    pub async fn set_leverage(&mut self, symbol: String, lever: String) -> ResponseData {
+        let params = serde_json::json!({
+             "instId": symbol,
+             "lever": lever,
+             "mgnMode": "cross"
+         });
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                "/account/set-leverage".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //设置持仓模式
+    pub async fn set_position_mode(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+             "posMode": "long_short_mode",
+         });
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                "/account/set-position-mode".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/
@@ -162,17 +328,18 @@ impl OkxSwapRest {
 
 
         // trace!("headers:{:?}", headers);
+        let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
         let start_time = chrono::Utc::now().timestamp_millis();
         let get_response = self.http_toll(
             format!("{}{}", prefix_url.clone(), request_url.clone()),
             method.to_string(),
-            params,
+            params.clone(),
             headers,
         ).await;
         let time_array = chrono::Utc::now().timestamp_millis() - start_time;
         self.delays.push(time_array);
         self.get_delay_info();
-        let res_data = Self::res_data_analysis(get_response);
+        let res_data = Self::res_data_analysis(get_response, base_url, params);
         res_data
     }
 
@@ -249,25 +416,52 @@ impl OkxSwapRest {
 
 
     //res_data 解析
-    pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>) -> ResponseData {
+    pub fn res_data_analysis(result: Result<ResponseData, reqwest::Error>, base_url: String, params: String) -> ResponseData {
+        // trace!("原始数据:{:?}",result);
         match result {
             Ok(res_data) => {
                 if res_data.code != "200" {
-                    res_data
+                    trace!("不等于200");
+                    let message: String = res_data.message;
+                    let json_value: serde_json::Value = serde_json::from_str(&message).unwrap();
+                    let code = json_value["code"].as_str().unwrap();
+                    let msg = json_value["msg"].as_str().unwrap();
+                    let error = ResponseData::new("".to_string(),
+                                                  format!("{}", code),
+                                                  format!("{}", msg),
+                                                  format!("请求地址:{},请求参数:{}", base_url, params));
+                    error
                 } else {
+                    trace!("等于200");
                     let body: String = res_data.data;
                     let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
 
                     let code = json_value["code"].as_str().unwrap();
-
-                    if code != "200000" {
-                        let msg = json_value["msg"].as_str().unwrap();
-                        let error = ResponseData::new("".to_string(), code.to_string(), msg.to_string(), "".parse().unwrap());
-                        error
-                    } else {
+                    if code == "0" {
                         let data = serde_json::to_string(&json_value["data"]).unwrap();
                         let success = ResponseData::new("".to_string(), "200".to_string(), "success".to_string(), data.parse().unwrap());
                         success
+                    } else if code == "1" || code == "0" {
+                        let msg = json_value["msg"].as_str().unwrap();
+                        let data = serde_json::to_string(&json_value["data"]).unwrap();
+
+                        let data_json: serde_json::Value = serde_json::from_str(&data).unwrap();
+                        let code_v = data_json[0]["sCode"].as_str().unwrap();
+                        let msg_v = data_json[0]["sMsg"].as_str().unwrap();
+                        // trace!("发生错误:??{:?}",data_json.to_string());
+                        let error = ResponseData::new("".to_string(),
+                                                      format!("{}", code_v),
+                                                      format!("{}:{}", msg, msg_v),
+                                                      format!("请求地址:{},请求参数:{}", base_url, params));
+                        error
+                    } else {
+                        let msg = json_value["msg"].as_str().unwrap();
+                        // trace!("发生错误:??{:?}",data_json.to_string());
+                        let error = ResponseData::new("".to_string(),
+                                                      format!("{}", code),
+                                                      format!("{}", msg),
+                                                      format!("请求地址:{},请求参数:{}", base_url, params));
+                        error
                     }
                 }
             }

+ 144 - 44
exchanges/src/okx_swap_ws.rs

@@ -1,13 +1,14 @@
 use std::collections::{BTreeMap};
-use std::{io, thread};
-use std::io::{Write};
+use std::{thread};
 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
 use std::time::Duration;
 use chrono::Utc;
 use serde_json::{json, Value};
 use ring::hmac;
 use tokio::sync::mpsc::Sender;
-use tracing::trace;
+use tracing::{error, info, trace};
 use crate::{proxy};
 use tungstenite::client::{AutoStream, connect_with_proxy, ProxyAutoStream};
 use tungstenite::{connect, Message, WebSocket};
@@ -15,6 +16,7 @@ use tungstenite::protocol::WebSocketConfig;
 use url::Url;
 use crate::proxy::ParsingDetail;
 use crate::response_base::ResponseData;
+use crate::utils::get_time_microsecond;
 
 pub enum OkxWsType {
     //订阅频道类型
@@ -26,11 +28,18 @@ pub enum OkxWsType {
 
 #[derive(Clone)]                        //订阅枚举
 pub enum OkxSubscribeType {
-    BuIndexCandle30m,
-    //
     PuIndexTickers,
+    PuBooks5,
+    Putrades,
+    PuBooks50L2tbt,
+    //
+    BuIndexCandle30m,
     //
+    PrBalanceAndPosition,
     PrAccount(String),
+    PrOrders,
+    PrPositions,
+
 }
 
 #[derive(Clone)]
@@ -67,10 +76,6 @@ impl OkxSwapWs {
                      sender: Sender<ResponseData>,
     ) -> OkxSwapWs
     {
-        if is_colo {
-            trace!("不支持高速通道")
-        }
-
         /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
         let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
 
@@ -87,6 +92,11 @@ impl OkxSwapWs {
             }
         };
 
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",request_url);
+        } else {
+            info!("走普通通道:{}",request_url);
+        }
         /*****返回结构体*******/
         OkxSwapWs {
             label,
@@ -107,7 +117,7 @@ impl OkxSwapWs {
         self.subscribe_types.extend(subscribe_types);
     }
     //自定义
-    pub async fn custom_subscribe(&mut self, b_array: Vec<String>)
+    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() {
@@ -117,7 +127,7 @@ impl OkxSwapWs {
             *symbol = symbol.replace("_", "-");
         }
         self.symbol_s = symbol_s;
-        self.run().await;
+        self.run(bool_v1).await;
     }
 
     /*******************************************************************************************************/
@@ -126,24 +136,63 @@ impl OkxSwapWs {
     //订阅枚举解析
     pub fn enum_to_string(symbol: String, subscribe_type: OkxSubscribeType) -> Value {
         match subscribe_type {
-            OkxSubscribeType::BuIndexCandle30m => {
+            OkxSubscribeType::PuIndexTickers => {
                 json!({
-                    "channel":"index-candle30m",
+                    "channel":"index-tickers",
                     "instId":symbol
                 })
             }
-            OkxSubscribeType::PuIndexTickers => {
+
+            OkxSubscribeType::PuBooks5 => {
                 json!({
-                    "channel":"index-tickers",
+                    "channel":"books5",
+                    "instId":symbol
+                })
+            }
+            OkxSubscribeType::Putrades => {
+                json!({
+                    "channel":"trades",
+                    "instId":symbol
+                })
+            }
+
+            OkxSubscribeType::BuIndexCandle30m => {
+                json!({
+                    "channel":"index-candle30m",
                     "instId":symbol
                 })
             }
+
             OkxSubscribeType::PrAccount(ccy) => {
                 json!({
                     "channel":"account",
                     "ccy":ccy
                 })
             }
+            OkxSubscribeType::PuBooks50L2tbt => {
+                json!({
+                    "channel":"books50-l2-tbt",
+                    "instId":symbol
+                })
+            }
+            OkxSubscribeType::PrBalanceAndPosition => {
+                json!({
+                    "channel":"balance_and_position"
+                })
+            }
+            OkxSubscribeType::PrOrders => {
+                json!({
+                    "channel":"orders",
+                    "instType":"SWAP",
+                    "instFamily":symbol
+                })
+            }
+            OkxSubscribeType::PrPositions => {
+                json!({
+                    "channel":"positions",
+                    "instType":"SWAP",
+                })
+            }
         }
     }
     //组装订阅数据
@@ -177,7 +226,7 @@ impl OkxSwapWs {
                 access_key = value.parse().unwrap();
             } else if key == "secret_key" {
                 secret_key = value.parse().unwrap();
-            } else if key == "passphrase" {
+            } else if key == "pass_key" {
                 passphrase = value.parse().unwrap();
             }
         }
@@ -199,7 +248,7 @@ impl OkxSwapWs {
                         });
 
             trace!("---login_json:{0}", login_json.to_string());
-            trace!("--登陆:{:?}", login_json);
+            trace!("--登陆:{}", login_json.to_string());
             login_json_str = login_json.to_string();
         }
         login_json_str
@@ -207,7 +256,7 @@ impl OkxSwapWs {
     /*******************************************************************************************************/
     /*****************************************socket基本*****************************************************/
     /*******************************************************************************************************/
-    async fn run(&mut self)
+    async fn run(&mut self, bool_v1: Arc<AtomicBool>)
     {
         //订阅信息组装
         let subscription = self.get_subscription();
@@ -234,7 +283,8 @@ impl OkxSwapWs {
                 match connect_with_proxy(request_url.clone(),
                                          proxy_address, websocket_config, max_redirects) {
                     Ok(ws) => {
-                        self.proxy_subscription(ws.0, subscription.clone()).await;
+                        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);
@@ -243,7 +293,8 @@ impl OkxSwapWs {
             } else {
                 match connect(request_url.clone()) {
                     Ok(ws) => {
-                        self.subscription(ws.0, subscription.clone()).await;
+                        let bool_v1_clone = Arc::clone(&bool_v1);
+                        self.subscription(bool_v1_clone, ws.0, subscription.clone()).await;
                     }
                     Err(err) => {
                         // 连接失败时执行的操作
@@ -253,12 +304,19 @@ impl OkxSwapWs {
                     }
                 };
             }
-            trace!("退出来咯")
+            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, mut web_socket: WebSocket<ProxyAutoStream>, subscription: String)
+    async fn proxy_subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<ProxyAutoStream>,
+                                subscription: String)
     {
         let lable = self.label.clone();
         /*****登陆***/
@@ -271,44 +329,60 @@ impl OkxSwapWs {
         web_socket.write_message(Message::Text(subscription))
             .unwrap();
         /*****消息溜***/
-        let mut stdout = io::stdout();
+        let mut ping_timeout = chrono::Utc::now().timestamp_millis();
         loop {
             let msg = web_socket.read_message();
             match msg {
                 Ok(Message::Text(text)) => {
-                    let res_data = Self::ok_text(lable.to_string(), text);
-                    if res_data.code == "-200" {
-                        writeln!(stdout, "订阅成功:{:?}", res_data.data).unwrap();
+                    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)) => {
-                    writeln!(stdout, "Ping-响应--{:?}", String::from_utf8(s.clone())).unwrap();
+                    trace!( "Ping-响应--{:?}", String::from_utf8(s.clone()));
                     let _ = web_socket.write_message(Message::Pong(Vec::from("pong")));
-                    writeln!(stdout, "回应-pong---{:?}", String::from_utf8(s.clone())).unwrap();
+                    trace!( "回应-pong---{:?}", String::from_utf8(s.clone()));
                 }
                 Ok(Message::Pong(s)) => {
                     // trace!("Pong-响应--{:?}", String::from_utf8(s));
-                    writeln!(stdout, "Pong-响应--{:?}", String::from_utf8(s.clone())).unwrap();
+                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
                 }
                 Ok(Message::Close(_)) => {
                     // trace!("socket 关闭: ");
-                    writeln!(stdout, "Close-响应").unwrap();
+                    trace!( "Close-响应");
                 }
-                Err(error) => {
+                Err(e) => {
                     // trace!("Error receiving message: {}", error);
-                    writeln!(stdout, "Err-响应{}", error).unwrap();
+                    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, mut web_socket: WebSocket<AutoStream>,
+    async fn subscription(&self, bool_v1: Arc<AtomicBool>, mut web_socket: WebSocket<AutoStream>,
                           subscription: String)
     {
         let lable = self.label.clone();
@@ -316,33 +390,54 @@ impl OkxSwapWs {
         web_socket.write_message(Message::Text(subscription))
             .unwrap();
         /*****消息溜***/
-        let mut stdout = io::stdout();
+        let mut  ping_timeout = chrono::Utc::now().timestamp_millis();
         loop {
             let msg = web_socket.read_message();
             match msg {
                 Ok(Message::Text(text)) => {
-                    let res_data = Self::ok_text(lable.to_string(), text);
-                    if res_data.code == "-200" {
-                        writeln!(stdout, "订阅成功:{:?}", res_data.data).unwrap();
+                    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));
+                    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));
+                    trace!( "Pong-响应--{:?}", String::from_utf8(s.clone()));
                 }
                 Ok(Message::Close(_)) => {
-                    trace!("socket 关闭: ");
+                    // trace!("socket 关闭: ");
+                    trace!( "Close-响应");
                 }
-                Err(error) => {
-                    trace!("Error receiving message: {}", error);
+                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();
     }
@@ -350,10 +445,15 @@ impl OkxSwapWs {
     //数据解析
     pub fn ok_text(lable: String, text: String) -> ResponseData
     {
+        // trace!("元数据:{}",text);
         let mut res_data = ResponseData::new(lable, "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("error") {
+            if json_value["event"].as_str() == Option::from("login") &&
+                json_value["code"].as_str() == Option::from("0") {
+                res_data.code = "-201".to_string();
+                res_data.message = format!("登陆成功!");
+            } else if json_value["event"].as_str() == Option::from("error") {
                 res_data.code = json_value["code"].to_string();
                 res_data.message = format!("错误:{}", json_value["msg"].to_string());
             } else if json_value["event"].as_str() == Option::from("subscribe") {
@@ -364,7 +464,7 @@ impl OkxSwapWs {
             }
         } else {
             if json_value.get("arg").is_some() && json_value.get("data").is_some() {
-                res_data.channel = json_value["arg"]["channel"].to_string();
+                res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
                 res_data.data = json_value["data"].to_string();
             } else {
                 res_data.data = text;

+ 8 - 3
exchanges/src/response_base.rs

@@ -1,6 +1,5 @@
-/**交易所返回数据处理之后,同意保存格式,为了内部其他接口调用*/
-pub enum ChannelType {}
 
+/**交易所返回数据处理之后,同意保存格式,为了内部其他接口调用*/
 #[derive(Debug, Clone)]
 pub struct ResponseData {
     pub label: String,
@@ -8,11 +7,12 @@ pub struct ResponseData {
     pub message: String,
     pub channel: String,
     pub data: String,
+    pub time: i64,
 }
 
 impl ResponseData {
     pub fn new(label: String, code: String, message: String, data: String) -> ResponseData {
-        ResponseData { label, code, message, data, channel: "".to_string() }
+        ResponseData { label, code, message, data, channel: "".to_string(), time: 0 }
     }
     pub fn error(label: String, message: String) -> ResponseData {
         ResponseData {
@@ -21,7 +21,12 @@ impl ResponseData {
             message: format!("{}", &message),
             data: "".to_string(),
             channel: "".to_string(),
+            time: 0,
         }
     }
+
+    pub fn to_string(&self) -> String {
+        format!("{:?}", self)
+    }
 }
 

+ 8 - 0
exchanges/src/utils.rs

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

+ 1 - 0
exchanges/tests/binance_spot_test.rs

@@ -7,6 +7,7 @@ use tracing::trace;
 use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
 use exchanges::response_base::ResponseData;
 
+// 账号密码
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
 

+ 15 - 7
exchanges/tests/binance_swap_test.rs

@@ -6,7 +6,6 @@ use tokio::try_join;
 use tracing::trace;
 use exchanges::binance_swap_rest::BinanceSwapRest;
 use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
-use exchanges::proxy;
 use exchanges::response_base::ResponseData;
 
 const ACCESS_KEY: &str = "";
@@ -18,7 +17,7 @@ const SECRET_KEY: &str = "";
 async fn ws_custom_subscribe() {
     global::log_utils::init_log_with_trace();
 
-    let mut bool_v1 = Arc::new(AtomicBool::new(true));
+    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);
@@ -71,17 +70,26 @@ async fn rest_get_account_test() {
     trace!(?rep_data)
 }
 
-//rest-查询订单
-#[tokio::test]
-async fn rest_get_order_test() {
+
+//rest-根据币对 撤销全部订单
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn rest_cancel_order_all_test() {
     global::log_utils::init_log_with_trace();
 
     let mut rest = get_rest();
+
+    let rep_data1 = rest.get_server_time().await;
+    trace!(?rep_data1);
+
+    trace!("开始时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
+    let rep_data = rest.cancel_order_all("BTCUSDT".to_string()).await;
+    trace!(?rep_data);
+    trace!("结束时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
 }
 
 
 fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> BinanceSwapWs {
-    let mut binance_ws = BinanceSwapWs::new(false,
+    let  binance_ws = BinanceSwapWs::new(false,
                                             btree_map,
                                             BinanceWsType::PublicAndPrivate,
                                             tx);
@@ -93,6 +101,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 mut ba_exc = BinanceSwapRest::new(false, btree_map);
+    let  ba_exc = BinanceSwapRest::new(false, btree_map);
     ba_exc
 }

+ 410 - 0
exchanges/tests/bitget_spot_test.rs

@@ -0,0 +1,410 @@
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use tokio::sync::mpsc::{channel, Sender};
+use tokio::try_join;
+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;
+
+const ACCESS_KEY: &str = "bg_f23d43b1e9d9cdf3ff34ce9efdd94d12";
+const SECRET_KEY: &str = "29a4be3fb17ef9df99ff1bfcf81cbb20544c7f99fa80160fbb09bfce54c0e9d3";
+const PASS_KEY: &str = "Bitget123123";
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+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, BitgetWsType::Public, tx).await;
+    ws.set_subscribe(vec![
+        BitgetSubscribeType::PuTicker,
+        BitgetSubscribeType::PuCandle1m,
+        BitgetSubscribeType::PuTrade,
+        BitgetSubscribeType::PuBooks5,
+    ]);
+    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();
+}
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+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;
+    ws.set_subscribe(vec![
+        BitgetSubscribeType::PrAccount,
+        BitgetSubscribeType::PrOrders,
+    ]);
+    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-获取系统时间
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_server_time().await;
+    trace!(?rep_data)
+}
+
+
+//rest-获取账户信息
+#[tokio::test]
+async fn rest_get_account_info_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_info().await;
+    trace!(?rep_data)
+}
+
+//rest-获取账户币种资产
+#[tokio::test]
+async fn rest_get_account_assets_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_account_assets().await;
+    trace!(?rep_data)
+}
+
+//rest-获取币种信息
+#[tokio::test]
+async fn rest_get_coins_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_coins("USDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取交易对信息
+#[tokio::test]
+async fn rest_get_symbols_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_symbols("BTCUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取现货VIP费率
+#[tokio::test]
+async fn rest_get_vip_fee_rate_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_vip_fee_rate().await;
+    trace!(?rep_data)
+}
+
+//rest-获取行情信息
+#[tokio::test]
+async fn rest_get_tickers_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_tickers("BTCUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取合并交易深度
+#[tokio::test]
+async fn rest_get_merge_depth_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_merge_depth("BTCUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取K线数据
+#[tokio::test]
+async fn rest_get_candles_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_candles("BTCUSDT".to_string(), "1min".to_string(), "1697701550192".to_string(), "1697701556192".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史K线数据
+#[tokio::test]
+async fn rest_get_history_candles_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_history_candles("BTCUSDT".to_string(), "1min".to_string(), "1697701556192".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取最近成交数据
+#[tokio::test]
+async fn rest_get_market_fills_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_market_fills("BTCUSDT".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史成交数据
+#[tokio::test]
+async fn rest_get_fills_history_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_market_fills_history("BTCUSDT".to_string(), "1697701550192".to_string(), "1697701556192".to_string(), "100".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-下单
+#[tokio::test]
+async fn rest_spot_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    //市价单
+    let mut rest = get_rest();
+    let params = serde_json::json!({
+            // "symbol":"CELRUSDT",
+            // "side":"sell",
+            // "orderType":"market",
+            // "force":"fok",
+            // "size":"887",
+            // "clientOid":"7d8zd4d_3",
+         });
+
+    //限价单
+    let params = serde_json::json!({
+            // "symbol":"CELRUSDT",
+            // "side":"buy",
+            // "orderType":"limit",
+            // "force":"gtc",
+            // "price":"0.01001",
+            // "size":"10",
+            // "clientOid":"7d8zd4d_z1",
+         });
+
+    let rep_data = rest.spot_order(params).await;
+    trace!(?rep_data)
+}
+
+
+//rest-撤单
+#[tokio::test]
+async fn rest_spot_cancel_order_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.spot_cancel_order("CELRUSDT".to_string(), "".to_string(), "1".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-批量撤单
+#[tokio::test]
+async fn rest_spot_cancel_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let v = serde_json::json!({
+            "orderId":"1073370944162058240",
+            "clientOid":"1073370944162058240"
+        });
+    let rep_data = rest.spot_cancel_orders("CELRUSDT".to_string(), vec![v]).await;
+    trace!(?rep_data)
+}
+
+//rest-按币对撤单
+#[tokio::test]
+async fn rest_spot_cancel_symbol_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.spot_cancel_symbol_orders("CELRUSDT".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取订单详情
+#[tokio::test]
+async fn rest_get_order_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_order("".to_string(), "1".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取当前委托列表
+#[tokio::test]
+async fn rest_get_unfilled_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_unfilled_orders("CELRUSDT".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+                                            "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史委托列表
+#[tokio::test]
+async fn rest_get_history_orders_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_history_orders("CELRUSDT".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+                                           "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取成交明细
+#[tokio::test]
+async fn rest_get_fills_test() {
+    global::log_utils::init_log_with_trace();
+    let mut rest = get_rest();
+    let rep_data = rest.get_fills("CELRUSDT".to_string(),
+                                  "1".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+                                  "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取成交明细
+#[tokio::test]
+async fn rest_spot_place_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    //限价-委托单
+    let params = serde_json::json!({
+         });
+
+    let rep_data = rest.spot_place_plan_order(params).await;
+    trace!(?rep_data)
+}
+
+//rest-修改计划委托
+#[tokio::test]
+async fn rest_update_place_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    //限价-委托单
+    let params = serde_json::json!({
+         });
+
+    let rep_data = rest.update_place_plan_order(params).await;
+    trace!(?rep_data)
+}
+
+//rest-撤销计划委托
+#[tokio::test]
+async fn rest_cancel_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_plan_order("32131".to_string(), "3211".to_string()).await;
+    trace!(?rep_data)
+}
+
+//rest-获取当前计划委托
+#[tokio::test]
+async fn rest_get_current_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_current_plan_order("CELRUSDT".to_string(),
+                                               "1".to_string(),
+                                               "".to_string(),
+                                               "".to_string(),
+                                               "".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-获取历史计划委托
+#[tokio::test]
+async fn rest_get_history_plan_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.get_history_plan_order("CELRUSDT".to_string(),
+                                               "1697701550192".to_string(),
+                                               "1697701580192".to_string(),
+                                               "100".to_string(),
+    ).await;
+    trace!(?rep_data)
+}
+
+//rest-批量撤销计划委托
+#[tokio::test]
+async fn rest_cancel_plan_orders_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_plan_orders(vec![]).await;
+    trace!(?rep_data)
+}
+
+
+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);
+    ku_ws
+}
+
+fn get_rest() -> BitgetSpotRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let mut ku_exc = BitgetSpotRest::new(false, btree_map);
+    ku_exc
+}

+ 53 - 25
exchanges/tests/gate_swap_test.rs

@@ -6,6 +6,7 @@ use tokio::try_join;
 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;
@@ -17,35 +18,62 @@ 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();
+    // 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();
 
-    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);
-            }
+
+    let json_str = r#"{"name": "Alice", "age": 30}"#;
+
+    // 解析 JSON 字符串并将其转换为一个 Rust 结构体
+    let parsed_data: Result<serde_json::Value, serde_json::Error> = serde_json::from_str(json_str);
+
+    match parsed_data {
+        Ok(data) => {
+            println!("Name: {}", data["name"]);
+            println!("Age: {}", data["age"]);
+        }
+        Err(e) => {
+            println!("Failed to parse JSON: {}", e);
         }
-    });
-    try_join!(t1,t2).unwrap();
+    }
 }
 
 
+//rest-设置持仓模式
+#[tokio::test]
+async fn rest_cancel_order_all_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.cancel_order_all().await;
+    println!("okx--设置持仓模式--{:?}", req_data);
+}
+
 fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> GateSwapWs {
     let mut binance_ws = GateSwapWs::new(false,
                                          btree_map,
@@ -54,11 +82,11 @@ fn get_ws(btree_map: BTreeMap<String, String>, tx: Sender<ResponseData>) -> Gate
     binance_ws
 }
 
-fn get_rest() -> BinanceSwapRest {
+fn get_rest() -> GateSwapRest {
     let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
     btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
     btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
 
-    let mut ba_exc = BinanceSwapRest::new(false, btree_map);
+    let mut ba_exc =  GateSwapRest::new(false, btree_map);
     ba_exc
 }

+ 43 - 1
exchanges/tests/kucoin_swap_test.rs

@@ -77,7 +77,7 @@ async fn ws_custom_subscribe_pr() {
 
 //rest-撤销全部订单
 #[tokio::test]
-async fn rest_get_server_time_test() {
+async fn rest_cancel_orders_test() {
     global::log_utils::init_log_with_trace();
 
     let mut rest = get_rest();
@@ -85,6 +85,48 @@ async fn rest_get_server_time_test() {
     trace!(?rep_data)
 }
 
+//rest-撤销全部订单
+#[tokio::test]
+async fn rest_cancel_order_all_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut rest = get_rest();
+    let rep_data = rest.cancel_order_all().await;
+    trace!(?rep_data)
+}
+
+
+//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 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);
+}
+
+//rest-设置自动开启补仓
+#[tokio::test]
+async fn rest_auto_deposit_test() {
+    global::log_utils::init_log_with_trace();
+
+    //设置是否自动补仓
+    let mut rest = get_rest();
+    let rep_data = rest.auto_deposit_status("BLZUSDTM".to_string(), false).await;
+    trace!("设置是否自动补仓{:?}",rep_data);
+}
+
+
 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;

+ 224 - 0
exchanges/tests/okx_swap_test.rs

@@ -0,0 +1,224 @@
+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 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;
+
+const ACCESS_KEY: &str = "";
+const SECRET_KEY: &str = "";
+const PASS_KEY: &str = "";
+
+
+//ws-订阅公共频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe_pu() {
+    global::log_utils::init_log_with_trace();
+
+    let 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;
+    ws.set_subscribe(vec![
+        OkxSubscribeType::PuIndexTickers,
+        OkxSubscribeType::PuBooks5,
+        OkxSubscribeType::Putrades,
+        OkxSubscribeType::PuBooks50L2tbt,
+    ]);
+
+    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();
+}
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn ws_custom_subscribe_bu() {
+    global::log_utils::init_log_with_trace();
+}
+
+//ws-订阅私有频道信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+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, 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-订单查询
+#[tokio::test]
+async fn rest_get_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_order("BTC-USDT".to_string(), "3333".to_string(), "".to_string()).await;
+    println!("okx--订单查询--{:?}", req_data);
+}
+
+
+//rest-未完成的订单
+#[tokio::test]
+async fn rest_get_incomplete_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_incomplete_order("BTC-USDT".to_string()).await;
+    println!("okx--未完成的订单--{:?}", req_data);
+}
+
+//rest-获取系统时间
+#[tokio::test]
+async fn rest_get_server_time_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_server_time().await;
+    println!("okx--获取系统时间--{:?}", req_data);
+}
+
+//rest-查看持仓信息
+#[tokio::test]
+async fn rest_get_positions_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_positions("SWA1P".to_string()).await;
+    println!("okx--查看持仓信息--{:?}", req_data);
+}
+
+//rest-获取单个产品行情信息
+#[tokio::test]
+async fn rest_get_ticker_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_ticker("BTC-USD".to_string()).await;
+    println!("okx--获取单个产品行情信息--{:?}", req_data);
+}
+
+//rest-查看账户余额
+#[tokio::test]
+async fn rest_get_balance_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_balance("BTC,ETH".to_string()).await;
+    println!("okx--查看账户余额--{:?}", req_data);
+}
+
+//rest-获取交易产品基础信息
+#[tokio::test]
+async fn rest_get_instruments_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_instruments().await;
+    println!("okx--获取交易产品基础信息--{:?}", req_data);
+}
+
+//rest-获取成交明细(近三天)
+#[tokio::test]
+async fn rest_get_trade_fills_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.get_trade_fills("".to_string()).await;
+    println!("okx--获取成交明细(近三天)--{:?}", req_data);
+}
+
+//rest-撤单
+#[tokio::test]
+async fn rest_cancel_order_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.cancel_order("BTC-USD".to_string(), "1111".to_string(), "".to_string()).await;
+    println!("okx--撤单--{:?}", req_data);
+}
+//rest-设置杠杆倍数
+#[tokio::test]
+async fn rest_set_leverage_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.set_leverage("BTC-USDT".to_string(), "5".to_string()).await;
+    println!("okx--设置杠杆倍数--{:?}", req_data);
+}
+//rest-设置持仓模式
+#[tokio::test]
+async fn rest_set_position_mode_test() {
+    global::log_utils::init_log_with_trace();
+
+    let mut ret = get_rest();
+    let req_data = ret.set_position_mode().await;
+    println!("okx--设置持仓模式--{:?}", req_data);
+}
+
+
+
+
+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);
+    ku_ws
+}
+
+fn get_rest() -> OkxSwapRest {
+    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
+    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
+    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
+
+    let mut okx_exc = OkxSwapRest::new(false, btree_map.clone());
+    okx_exc
+}

+ 45 - 44
exchanges/tests/test.rs

@@ -28,6 +28,7 @@ async fn test_import() {
         trace!("检测有代理配置,配置走代理");
     }
 
+
     //获取代理
     // demo_get_http_proxy();
 
@@ -37,13 +38,13 @@ async fn test_import() {
     // demo_rest_ba().await;
 
     //gate-rest -账户信息
-    demo_rest_gate().await;
+    // demo_rest_gate().await;
     //gate-ws-public-private频道
     // demo_ws_gate().await;
 
 
     //kucoin_rest -账户信息
-    // demo_rest_kucoin().await;
+    demo_rest_kucoin().await;
     //Kucoin-ws--公共频道
     // demo_ws_kucoin_pu().await;
     //Kucoin-ws--私有频道
@@ -173,14 +174,14 @@ async fn test_import() {
 
 
 async fn demo_okx_rest() {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), "".to_string());
-    btree_map.insert("secret_key".to_string(), "".to_string());
-    btree_map.insert("pass_key".to_string(), "".to_string());
-
-    let mut okx_rest = OkxSwapRest::new(false, btree_map);
-    let res_data = okx_rest.get_account("BTC".to_string()).await;
-    trace!("okx_rest-rest - get_account- {:?}", res_data);
+    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // btree_map.insert("access_key".to_string(), "".to_string());
+    // btree_map.insert("secret_key".to_string(), "".to_string());
+    // btree_map.insert("pass_key".to_string(), "".to_string());
+    //
+    // let mut okx_rest = OkxSwapRest::new(false, btree_map);
+    // let res_data = okx_rest.get_account("BTC".to_string()).await;
+    // trace!("okx_rest-rest - get_account- {:?}", res_data);
 }
 
 async fn demo_ws_gate() {
@@ -242,41 +243,41 @@ fn demo_so() {
 
 async fn demo_ws_okx_pu() {
 
-    let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Public, tx);
-    ku_ws.set_subscribe(vec![OkxSubscribeType::PuIndexTickers]);
-    let t1 = tokio::spawn(async move {
-        ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
-        let mut stdout = std::io::stdout();
-        loop {
-            if let Ok(received) = rx.try_recv() {
-                writeln!(stdout, "age: {:?}", received).unwrap();
-            }
-        }
-    });
-    try_join!(t1,t2).unwrap();
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Public, tx);
+    // ku_ws.set_subscribe(vec![OkxSubscribeType::PuIndexTickers]);
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     let mut stdout = std::io::stdout();
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             writeln!(stdout, "age: {:?}", received).unwrap();
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
 }
 
 async fn demo_ws_okx_bu() {
-    let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    let (tx, mut rx) = channel(1024);
-    let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Business, tx);
-    ku_ws.set_subscribe(vec![OkxSubscribeType::BuIndexCandle30m]);
-    let t1 = tokio::spawn(async move {
-        ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
-    });
-    let t2 = tokio::spawn(async move {
-        let mut stdout = std::io::stdout();
-        loop {
-            if let Ok(received) = rx.try_recv() {
-                writeln!(stdout, "age: {:?}", received).unwrap();
-            }
-        }
-    });
-    try_join!(t1,t2).unwrap();
+    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
+    // let (tx, mut rx) = channel(1024);
+    // let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Business, tx);
+    // ku_ws.set_subscribe(vec![OkxSubscribeType::BuIndexCandle30m]);
+    // let t1 = tokio::spawn(async move {
+    //     ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
+    // });
+    // let t2 = tokio::spawn(async move {
+    //     let mut stdout = std::io::stdout();
+    //     loop {
+    //         if let Ok(received) = rx.try_recv() {
+    //             writeln!(stdout, "age: {:?}", received).unwrap();
+    //         }
+    //     }
+    // });
+    // try_join!(t1,t2).unwrap();
 }
 
 async fn demo_ws_kucoin_pr() {
@@ -349,8 +350,8 @@ async fn demo_rest_kucoin() {
     // trace!("kucoin_exc-rest - get_market_details- {:?}", res_data);
     // let res_data = kucoin_exc.get_ticker("ROSEUSDTM".to_string()).await;
     // trace!("kucoin_exc-rest - get_ticker- {:?}", res_data);
-    let res_data = kucoin_exc.get_orders("active".to_string(),
-                                         "ROSEUSDTM".to_string()).await;
+    let res_data = kucoin_exc.get_orders("".to_string(),
+                                         "".to_string()).await;
     trace!("kucoin_exc-rest - get_orders- {:?}", res_data);
     // let res_data = kucoin_exc.get_positions("USDT".to_string()).await;
     // trace!("kucoin_exc-rest - get_positions- {:?}", res_data);

+ 1 - 0
global/.gitignore

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

+ 8 - 3
global/Cargo.toml

@@ -9,8 +9,13 @@ edition = "2021"
 rust_decimal = "1.32.0"
 rust_decimal_macros = "1.32.0"
 tracing = "0.1"
-tracing-subscriber = "0.3.17"
-toml = "0.5"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+tracing-appender = "0.2.2"
+toml = "0.5.11"
 serde = "1.0.183"
 serde_derive = "1.0"
-serde_json = "1.0.104"
+serde_json = "1.0.104"
+chrono = "0.4.26"
+tokio = { version = "1.31.0", features = ["full"] }
+base64 = "0.13.0"
+reqwest = "0.11.22"

+ 1 - 0
global/src/lib.rs

@@ -1,3 +1,4 @@
 pub mod public_params;
 pub mod log_utils;
 pub mod params;
+pub mod trace_stack;

+ 126 - 17
global/src/log_utils.rs

@@ -1,30 +1,139 @@
-use tracing_subscriber::{FmtSubscriber};
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::io;
+use chrono::{Datelike, FixedOffset, Timelike, Utc};
+use tracing::{Event, Subscriber, warn};
+use tracing_appender::non_blocking::WorkerGuard;
+use tracing_subscriber::{fmt, Layer};
+use tracing_subscriber::fmt::format::Writer;
+use tracing_subscriber::fmt::time::FormatTime;
+use tracing_subscriber::layer::{Context, SubscriberExt};
+use reqwest::{Client};
+use rust_decimal::prelude::ToPrimitive;
+use tracing::field::{Field, Visit};
+
+// 用來格式化日誌的輸出時間格式
+struct LocalTimer;
+
+impl FormatTime for LocalTimer {
+    fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
+        let now = Utc::now().with_timezone(&FixedOffset::east_opt(8 * 3600).unwrap());
+        write!(
+            w,
+            "{:04}-{:02} {:02}:{:02}:{:02}.{:03}",
+            now.month(),
+            now.day(),
+            now.hour(),
+            now.minute(),
+            now.second(),
+            now.nanosecond() / 1e6.to_u32().unwrap()
+        )
+    }
+}
+
+struct ErrorMessageVisitor {
+    message: String
+}
+
+impl Visit for ErrorMessageVisitor {
+    fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
+        if field.name() == "message" {
+            self.message = format!("{:?}", value);
+        }
+    }
+}
+
+
+// 错误报告发送到指定服务器
+struct ReportingLayer {
+    account_name: String,
+}
+impl<S> Layer<S> for ReportingLayer
+    where
+        S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
+{
+    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
+        if event.metadata().level() == &tracing::Level::ERROR {
+            let mut visitor = ErrorMessageVisitor {
+                message: String::new()
+            };
+            event.record(&mut visitor);
+
+            let msg = format!("account={}, type=error, msg={}", self.account_name.clone(), visitor.message);
+            send_remote_err_log(msg)
+        }
+    }
+}
+
+pub fn send_remote_err_log(msg: String) {
+    tokio::spawn(async move {
+        let encoded_str = base64::encode(msg.clone());
+        let mut request_json_data = HashMap::new();
+        request_json_data.insert("serverName", "As");
+        request_json_data.insert("data", encoded_str.as_str());
+
+        let res = Client::new().post("https://hhh.liangjiang.cc/api/log/addError?key=d64a8sc874sa8c4as5")
+            .json(&request_json_data)
+            .send()
+            .await;
+
+        match res {
+            Ok(_resp) => {
+                // let body = _resp.text().await.unwrap();
+            }
+            Err(err) => {
+                warn!("log的error监听器发送远端报错失败:{:?}", err);
+            }
+        }
+    });
+}
 
 pub fn init_log_with_debug() {
-    final_init(tracing::Level::DEBUG)
+    let _ = final_init(tracing::Level::DEBUG.as_str(), 0, "test".to_string());
 }
 
 pub fn init_log_with_trace() {
-    final_init(tracing::Level::TRACE)
+    let _ = final_init(tracing::Level::TRACE.as_str(), 0, "test".to_string());
 }
 
 pub fn init_log_with_info() {
-    final_init(tracing::Level::INFO)
+    let _ = final_init(tracing::Level::INFO.as_str(), 0, "test".to_string());
 }
 
-pub fn final_init(level: tracing::Level) {
-    // let filter = EnvFilter::from_default_env()
-    //     .add_directive("exchanges=trace".parse().unwrap())
-    //     .add_directive("global=trace".parse().unwrap())
-    //     .add_directive("standard=trace".parse().unwrap())
-    //     .add_directive("tests=trace".parse().unwrap())
-    //     .add_directive("strategy=trace".parse().unwrap());
+pub fn final_init(level: &str, port: u32, account_name: String) -> WorkerGuard {
+    let mut path = String::new();
+    path.push_str("./logs");
+    path.push_str(port.to_string().as_str());
+
+    let file_appender = tracing_appender::rolling::daily(path, "as-debug.log");
+    let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
+
+    let fmt_layer = fmt::layer()
+        .with_timer(LocalTimer)
+        .with_target(true)
+        .with_level(true)
+        .with_writer(io::stdout)
+        .with_span_events(fmt::format::FmtSpan::FULL);
+
+    let file_layer = fmt::layer()
+        .with_timer(LocalTimer)
+        .with_target(true)
+        .with_ansi(false)
+        .with_level(true)
+        .with_writer(non_blocking.clone())
+        .with_span_events(fmt::format::FmtSpan::FULL);
+
+    let reporting_layer = ReportingLayer {
+        account_name
+    };
+
+    let layer = tracing_subscriber::Registry::default()
+        .with(fmt_layer)
+        .with(file_layer)
+        .with(reporting_layer)
+        .with(tracing_subscriber::EnvFilter::new(level));
 
-    let subscriber = FmtSubscriber::builder()
-        // .with_env_filter(filter)
-        .with_max_level(level)
-        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
-        .finish();
+    tracing::subscriber::set_global_default(layer).unwrap();
 
-    tracing::subscriber::set_global_default(subscriber).unwrap();
+    return guard;
 }

+ 4 - 7
global/src/params.rs

@@ -27,25 +27,22 @@ pub struct Params {
     pub close: Decimal,
     // 杠杆大小
     pub lever_rate: Decimal,
-
-    pub interval: Decimal,
+    // 现货底仓
+    pub hold_coin: Decimal,
+    // quant的run_strategy函数,用于定期检查使用
+    pub interval: u64,
     // 参考盘口
     pub ref_exchange: Vec<String>,
     // 参考币种
     pub ref_pair: Vec<String>,
     // 账户资金使用比例
     pub used_pct: Decimal,
-    // 默认第n参考盘口
-    pub index: i8,
-    pub hold_coin: Decimal,
     // 止损比例 默认0.02  0.02 = 2%
     pub stop_loss: Decimal,
     // 平滑系数 默认0.999
     pub gamma: Decimal,
     // 分批建仓功能 小资金建议1 大资金建议3 默认 1
     pub grid: i8,
-    // 允许的每秒下单次数
-    pub place_order_limit: i64,
     // 是否启用colocation技术, 1开启,0关闭 默认0
     pub colo: i8,
     // 日志级别,从低到高依次是:[trace, debug, info, warn, error]

+ 2 - 1
global/src/public_params.rs

@@ -21,13 +21,14 @@ pub const BASIC_LIMIT:i64 = 100;
 pub const GATE_SPOT_LIMIT:i64 = 10;
 pub const GATE_USDT_SWAP_LIMIT:i64 = 100;
 pub const KUCOIN_SPOT_LIMIT:i64 = 15;
-pub const KUCOIN_USDT_SWAP_LIMIT:i64 = 8;
+pub const KUCOIN_USDT_SWAP_LIMIT:i64 = 7;
 pub const BINANCE_USDT_SWAP_LIMIT:i64 = 5;
 pub const BINANCE_SPOT_LIMIT:i64 = 2;
 pub const COINEX_SPOT_LIMIT:i64 = 20;
 pub const COINEX_USDT_SWAP_LIMIT:i64 = 20;
 pub const OKEX_USDT_SWAP_LIMIT:i64 = 30;
 pub const BITGET_USDT_SWAP_LIMIT:i64 = 10;
+pub const BITGET_USDT_SPOT_LIMIT:i64 = 10;
 pub const BYBIT_USDT_SWAP_LIMIT:i64 = 1;
 pub const MEXC_SPOT_LIMIT:i64 = 333;
 pub const RATIO:i64 = 4;

+ 118 - 0
global/src/trace_stack.rs

@@ -0,0 +1,118 @@
+use std::fmt;
+use std::fmt::{Formatter};
+use chrono::Utc;
+use rust_decimal::prelude::ToPrimitive;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TraceStack {
+    pub after_network: i64,                 // 到达网络层时间
+    pub before_quant: i64,                  // quant层执行开始时间(也是通道+锁走完后的时间)
+    pub after_quant: i64,                   // quant层执行结束时间
+
+    pub before_format: i64,                 // 开始格式化时间
+    pub after_format: i64,                  // 结束格式化时间
+
+    pub before_strategy: i64,               // 计算层开始时间
+    pub after_strategy: i64,                // 计算层结束时间
+
+    pub before_send_thread: i64,            // 发送指令时时间(进入协程前)
+    pub before_send: i64,                   // 发送指令时时间(进入协程后)
+    pub after_send: i64,                    // 发送指令结束时间
+
+    pub source: String,                     // 订单来源[depth|order]
+    pub order_command: String               // 订单发送时的command
+}
+
+impl TraceStack {
+    pub fn on_network(&mut self, after_network: i64) {
+        self.after_network = after_network;
+    }
+
+    pub fn on_before_quant(&mut self) {
+        self.before_quant = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_after_quant(&mut self) {
+        self.after_quant = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_before_format(&mut self) {
+        self.before_format = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_after_format(&mut self) {
+        self.after_format = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_before_strategy(&mut self) {
+        self.before_strategy = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_after_strategy(&mut self) {
+        self.after_strategy = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_before_send_thread(&mut self) {
+        self.before_send_thread = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_before_send(&mut self) {
+        self.before_send = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_after_send(&mut self) {
+        self.after_send = Utc::now().timestamp_micros();
+    }
+
+    pub fn on_depth(&mut self) {
+        self.source = "depth".to_string();
+    }
+
+    pub fn on_order(&mut self) {
+        self.source = "order".to_string();
+    }
+
+    pub fn on_order_command(&mut self, command_str: String) {
+        self.order_command = command_str;
+    }
+
+    pub fn on_special(mut self, event_str: String) -> Self {
+        self.after_network = 1;
+        self.source = event_str;
+
+        self
+    }
+}
+
+impl fmt::Display for TraceStack {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "订单来源:{},通道+锁耗时{}微秒,数据格式化耗时{}微秒,计算层执行耗时{}微秒, 进入协程耗时{}微秒, 发送订单耗时(发送-服务器处理-响应到本地){}毫秒,总共耗时{}毫秒。",
+            self.source,
+            self.before_quant - self.after_network,
+            self.after_format - self.before_format,
+            self.after_strategy - self.before_strategy,
+            self.before_send - self.before_send_thread,
+            (self.after_send - self.before_send).to_f64().unwrap() / 1000.0,
+            (self.after_send - self.after_network).to_f64().unwrap() / 1000.0
+        )
+    }
+}
+
+impl Default for TraceStack {
+    fn default() -> Self {
+        TraceStack {
+            after_network: 0,
+            before_format: 0,
+            after_format: 0,
+            before_quant: 0,
+            after_quant: 0,
+            before_strategy: 0,
+            after_strategy: 0,
+            before_send_thread: 0,
+            before_send: 0,
+            after_send: 0,
+            source: "".to_string(),
+            order_command: "".to_string(),
+        }
+    }
+}

+ 33 - 0
global/tests/appender_test.rs

@@ -0,0 +1,33 @@
+use tracing::{info};
+use tracing_subscriber::{fmt};
+use tracing_subscriber::prelude::*;
+use std::io;
+
+#[tokio::test]
+async fn appender_test() {
+    let file_appender = tracing_appender::rolling::daily("./logs", "tracing.log");
+    let (non_blocking, _) = tracing_appender::non_blocking(file_appender);
+
+    let fmt_layer = fmt::layer()
+        .with_target(false)
+        .with_level(true)
+        .with_writer(io::stdout);
+
+    let file_layer = fmt::layer()
+        .with_target(true)
+        .with_ansi(false)
+        .with_level(true)
+        .with_writer(non_blocking.clone());
+
+    let subscriber = tracing_subscriber::Registry::default()
+        .with(fmt_layer)
+        .with(file_layer)
+        .with(tracing_subscriber::EnvFilter::new("info"));
+
+    let _ = tracing::subscriber::set_global_default(subscriber);
+    let number = 42;
+    info!(answer = number, "the answer to life, the universe, and everything");
+
+    // 使 main 函数“等待”一段时间,增大日志被正确写入的机会
+    tokio::time::sleep(std::time::Duration::from_secs(10)).await;
+}

+ 1 - 3
src/control_c.rs

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

+ 45 - 14
src/main.rs

@@ -2,37 +2,68 @@ mod server;
 mod control_c;
 mod quant_libs;
 
-use std::str::FromStr;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::time::Duration;
-use tracing::{info};
+use tracing::{info, warn};
+use tracing_appender::non_blocking::WorkerGuard;
+use global::log_utils::send_remote_err_log;
 use global::params::Params;
 
 // 日志级别配置
-fn log_level_init(log_str: String) {
-    let tracing_log_level = tracing::Level::from_str(log_str.as_str()).unwrap();
-    info!("日志级别读取成功:{}。", tracing_log_level);
-    global::log_utils::final_init(tracing_log_level);
+fn log_level_init(log_str: String, port: u32, account_name: String) -> WorkerGuard {
+    info!("日志级别读取成功:{}。", log_str);
+    global::log_utils::final_init(log_str.as_str(), port, account_name)
+}
+
+// 获取本地配置
+fn read_params() -> Params {
+    let mut path = "config.toml";
+
+    let args: Vec<String> = std::env::args().collect();
+
+    for arg in &args {
+        if !arg.contains("--config") {
+            continue;
+        }
+
+        let p: Vec<&str> = arg.split("=").collect();
+        path = p[1];
+    }
+
+    let params = Params::new(path).unwrap();
+
+    return params;
 }
 
 #[tokio::main(flavor = "multi_thread", worker_threads = 2)]
 async fn main() {
-    // 获取本地配置
-    let params = Params::new("config.toml").unwrap();
+    // 日志级别配置
+    let params = read_params();
+    // 日志级别配置
+    let _guard = log_level_init(params.log_level.clone(), params.port.clone(), params.account_name.clone());
     info!("配置读取成功:{:?}。", params);
     // 主进程控制
     let running = Arc::new(AtomicBool::new(true));
     // ws退出程序
     let ws_running = Arc::new(AtomicBool::new(true));
-    // 日志级别配置
-    log_level_init(params.log_level.clone());
     // quant初始化动作
-    let quant_arc = quant_libs::init(params, ws_running.clone(), running.clone()).await;
+    let quant_arc = quant_libs::init(params.clone(), ws_running.clone(), running.clone()).await;
     // 初始化中控服务
-    server::run_server(6000, running.clone(), quant_arc.clone());
-    // 退出检查程序
-    let running = control_c::exit_handler(running);
+    server::run_server(params.port.clone(), running.clone(), quant_arc.clone());
+    // ctrl c退出检查程序
+    control_c::exit_handler(running.clone());
+
+    // panic错误捕获,panic级别的错误直接退出
+    let account_name_clone = params.account_name.clone();
+    let panic_running = running.clone();
+    std::panic::set_hook(Box::new(move |panic_info| {
+        let msg = format!("account={}, type=panic, msg={:?}, location={:?}",
+                          account_name_clone, panic_info.to_string(), panic_info.location());
+        warn!("{}", msg);
+        send_remote_err_log(msg);
+        panic_running.store(false, Ordering::Relaxed);
+    }));
 
     // 每一秒检查一次程序是否结束
     while running.load(Ordering::Relaxed) {

+ 18 - 7
src/quant_libs.rs

@@ -6,9 +6,11 @@ use strategy::{exchange_disguise, quant};
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool};
 use std::time::Duration;
+use chrono::Utc;
 use tokio::sync::{mpsc, Mutex};
 use tracing::{error, info};
 use global::params::Params;
+use global::trace_stack::TraceStack;
 use standard::Order;
 use strategy::model::OrderInfo;
 
@@ -29,6 +31,7 @@ pub async fn init(params: Params, ws_running: Arc<AtomicBool>, running: Arc<Atom
     info!("quant初始化……");
     quant_obj.before_trade().await;
     let quant_arc = Arc::new(Mutex::new(quant_obj));
+
     // 参考交易所
     exchange_disguise::run_reference_exchange(ws_running.clone(),params.ref_exchange.get(0).unwrap().clone(), quant_arc.clone(), ref_name, params.ref_pair.clone(), exchange_params.clone()).await;
     // 交易交易所
@@ -46,9 +49,15 @@ pub async fn init(params: Params, ws_running: Arc<AtomicBool>, running: Arc<Atom
 
             match order_receiver.recv().await {
                 Some(order) => {
+                    // 刚下的订单有可能会成交,所以有几率触发后续订单逻辑,所以这里也是一个订单触发逻辑
+                    let mut trace_stack = TraceStack::default();
+                    trace_stack.on_network(Utc::now().timestamp_micros());
+                    trace_stack.on_before_quant();
+
                     if order.status != "NULL" {
+                        trace_stack.on_before_format();
                         let mut quant = order_handler_quant_arc.lock().await;
-
+                        // let mut delay_time_lock_instance = delay_time_lock.lock().await;
                         let order_info = OrderInfo {
                             symbol: "".to_string(),
                             amount: order.amount.abs(),
@@ -62,9 +71,11 @@ pub async fn init(params: Params, ws_running: Arc<AtomicBool>, running: Arc<Atom
                             create_time: 0,
                             status: order.status,
                             fee: Default::default(),
+                            trace_stack: order.trace_stack.clone(),
                         };
+                        trace_stack.on_after_format();
 
-                        quant.update_local_order(order_info.clone());
+                        quant.update_local_order(order_info.clone(), trace_stack);
                     }
                 },
                 None => {
@@ -74,16 +85,16 @@ pub async fn init(params: Params, ws_running: Arc<AtomicBool>, running: Arc<Atom
         }
     });
 
-    // let error_handler_quant_arc = quant_arc.clone();
+    let _error_handler_quant_arc = quant_arc.clone();
     tokio::spawn(async move {
         loop {
             tokio::time::sleep(Duration::from_millis(1)).await;
 
             match error_receiver.recv().await {
-                Some(error) => {
-                    // let quant = error_handler_quant_arc.lock().await;
-
-                    error!("main: 订单出现错误{:?}", error);
+                Some(_error) => {
+                    // let mut quant = _error_handler_quant_arc.lock().await;
+                    // error!("main: 订单出现错误{:?}", _error);
+                    // quant.strategy._print_summary();
                 },
                 None => {
                     error!("Error channel has been closed!");

+ 9 - 15
src/server.rs

@@ -1,18 +1,18 @@
 use std::ops::Div;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
-use serde::{Deserialize, Serialize};
+use serde::{Serialize};
 use strategy::quant::Quant;
 use actix_web::{web, App, HttpResponse, HttpServer, Responder, post, get};
 use rust_decimal::Decimal;
 use tokio::sync::Mutex;
-use tracing::{debug, info};
+use tracing::{info};
 
 
-#[derive(Deserialize, Debug)]
-struct InputData {
-    stop: bool,
-}
+// #[derive(Deserialize, Debug)]
+// struct InputData {
+//     stop: i64,
+// }
 
 #[derive(Serialize, Clone)]
 struct AccountInfo {
@@ -72,15 +72,9 @@ async fn get_account(arcs: web::Data<Arcs>) -> impl Responder {
 
 // 句柄 POST 请求
 #[post("/exit")]
-async fn on_change(arcs: web::Data<Arcs>, input: web::Json<InputData>) -> impl Responder {
-    debug!(?input);
-
-    if input.stop {
-        arcs.running.store(false, Ordering::Relaxed);
-        HttpResponse::Ok().body(format!("程序已收到退出信号,将在10秒内退出。"))
-    } else {
-        HttpResponse::ServiceUnavailable().body(format!("程序没有收到正确的信号:{:?}。", input))
-    }
+async fn on_change(arcs: web::Data<Arcs>) -> impl Responder {
+    arcs.running.store(false, Ordering::Relaxed);
+    HttpResponse::Ok().body(format!("程序已收到退出信号,将在5秒内退出。"))
 }
 
 pub fn run_server(port: u32, running: Arc<AtomicBool>, quant_arc: Arc<Mutex<Quant>>) {

+ 1 - 0
standard/.gitignore

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

+ 48 - 34
standard/src/binance_spot.rs

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

+ 136 - 36
standard/src/binance_swap.rs

@@ -2,13 +2,15 @@ use std::collections::BTreeMap;
 use std::io::{Error, ErrorKind};
 use std::result::Result;
 use std::str::FromStr;
-
 use async_trait::async_trait;
 use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
 use tokio::sync::mpsc::Sender;
+use tracing::{error, warn};
 use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, utils, PositionModeEnum};
 use exchanges::binance_swap_rest::BinanceSwapRest;
+use global::trace_stack::TraceStack;
 
 #[allow(dead_code)]
 #[derive(Clone)]
@@ -77,7 +79,7 @@ impl Platform for BinanceSwap {
             let result = res_data_json["serverTime"].to_string();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 获取账号信息
@@ -90,6 +92,7 @@ impl Platform for BinanceSwap {
             let balance_info = res_data_json.iter().find(|item| item["asset"].as_str().unwrap().to_string() == symbol_array[1].to_string());
             match balance_info {
                 None => {
+                    error!("格式化Binance账号信息错误!\nget_account: balance_info={:?}", balance_info);
                     panic!("格式化Binance账号信息错误!\nget_account: balance_info={:?}", balance_info)
                 }
                 Some(value) => {
@@ -97,45 +100,51 @@ impl Platform for BinanceSwap {
                     let available_balance = Decimal::from_str(value["availableBalance"].as_str().unwrap()).unwrap();
                     let frozen_balance = balance - available_balance;
                     let result = Account {
+                        coin: value["asset"].as_str().unwrap().to_string(),
                         balance,
                         available_balance,
                         frozen_balance,
-                        stocks: dec!(0),
-                        available_stocks: dec!(0),
-                        frozen_stocks: dec!(0),
+                        stocks: Decimal::ZERO,
+                        available_stocks: Decimal::ZERO,
+                        frozen_stocks: Decimal::ZERO,
                     };
                     Ok(result)
                 }
             }
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string()))
+    }
+
     // 获取仓位信息
     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
         let symbol_format = utils::format_symbol(self.symbol.clone(), "");
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_position_risk(symbol_format).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let result = res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_position_risk("".to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let result = res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -157,7 +166,28 @@ impl Platform for BinanceSwap {
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let res_data = self.request.get_book_ticker(symbol_format).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = Ticker {
+                time: res_data_json["time"].as_i64().unwrap(),
+                high: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
+                last: dec!(-1),
+                volume: dec!(-1),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -171,6 +201,50 @@ impl Platform for BinanceSwap {
             let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
             match market_info {
                 None => {
+                    error!("Binance:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
+                    panic!("Binance:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
+                }
+                Some(value) => {
+                    let base_asset = value["baseAsset"].as_str().unwrap_or("").to_string();
+                    let quote_asset = value["quoteAsset"].as_str().unwrap_or("").to_string();
+
+                    let filter_array = value["filters"].as_array().unwrap().clone();
+                    let price_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "PRICE_FILTER").unwrap();
+                    let lot_size_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "LOT_SIZE").unwrap();
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset, quote_asset),
+                        base_asset,
+                        quote_asset,
+                        tick_size: Decimal::from_str(&price_filter["tickSize"].as_str().unwrap()).unwrap(),
+                        amount_size: Decimal::from_str(lot_size_filter["stepSize"].as_str().unwrap()).unwrap(),
+                        price_precision: Decimal::from_f64(value["pricePrecision"].as_f64().unwrap()).unwrap(),
+                        amount_precision: Decimal::from_f64(value["quantityPrecision"].as_f64().unwrap()).unwrap(),
+                        min_qty: Decimal::from_str(lot_size_filter["minQty"].as_str().unwrap()).unwrap(),
+                        max_qty: Decimal::from_str(lot_size_filter["maxQty"].as_str().unwrap()).unwrap(),
+                        min_notional: Decimal::from_str(price_filter["minPrice"].as_str().unwrap()).unwrap(),
+                        max_notional: Decimal::from_str(price_filter["maxPrice"].as_str().unwrap()).unwrap(),
+                        ct_val: Decimal::ONE,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let res_data = self.request.get_exchange_info().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let symbols: Vec<serde_json::Value> = res_data_json["symbols"].as_array().unwrap().clone();
+            let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("Binance:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
                     panic!("Binance:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
                 }
                 Some(value) => {
@@ -186,20 +260,20 @@ impl Platform for BinanceSwap {
                         base_asset,
                         quote_asset,
                         tick_size: Decimal::from_str(&price_filter["tickSize"].as_str().unwrap()).unwrap(),
-                        amount_size: dec!(1),
-                        price_precision: Decimal::from_str(&value["pricePrecision"].to_string()).unwrap(),
-                        amount_precision: Decimal::from_str(&value["quantityPrecision"].to_string()).unwrap(),
+                        amount_size: Decimal::from_str(lot_size_filter["stepSize"].as_str().unwrap()).unwrap(),
+                        price_precision: Decimal::from_f64(value["pricePrecision"].as_f64().unwrap()).unwrap(),
+                        amount_precision: Decimal::from_f64(value["quantityPrecision"].as_f64().unwrap()).unwrap(),
                         min_qty: Decimal::from_str(lot_size_filter["minQty"].as_str().unwrap()).unwrap(),
                         max_qty: Decimal::from_str(lot_size_filter["maxQty"].as_str().unwrap()).unwrap(),
                         min_notional: Decimal::from_str(price_filter["minPrice"].as_str().unwrap()).unwrap(),
                         max_notional: Decimal::from_str(price_filter["maxPrice"].as_str().unwrap()).unwrap(),
-                        ct_val: Default::default(),
+                        ct_val: Decimal::ONE,
                     };
                     Ok(result)
                 }
             }
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -211,7 +285,10 @@ impl Platform for BinanceSwap {
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
 
             let status = res_data_json["status"].as_str().unwrap();
-            let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else { panic!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status) };
+            let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else {
+                error!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status);
+                panic!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status)
+            };
             let result = Order {
                 id: res_data_json["orderId"].to_string(),
                 custom_id: res_data_json["clientOrderId"].as_str().unwrap().parse().unwrap(),
@@ -221,10 +298,11 @@ impl Platform for BinanceSwap {
                 avg_price: Decimal::from_str(res_data_json["avgPrice"].as_str().unwrap()).unwrap(),
                 status: custom_status,
                 order_type: res_data_json["type"].as_str().unwrap().parse().unwrap(),
+                trace_stack: TraceStack::default().on_special("301 binance_swap".to_string()),
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -237,7 +315,10 @@ impl Platform for BinanceSwap {
             let order_info: Vec<_> = res_data_json.iter().filter(|item| item["contract"].as_str().unwrap_or("") == self.symbol).collect();
             let result = order_info.iter().map(|&item| {
                 let status = item["status"].as_str().unwrap();
-                let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else { panic!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status) };
+                let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else {
+                    error!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status);
+                    panic!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status)
+                };
                 Order {
                     id: item["orderId"].to_string(),
                     custom_id: item["clientOrderId"].as_str().unwrap().parse().unwrap(),
@@ -247,44 +328,63 @@ impl Platform for BinanceSwap {
                     avg_price: Decimal::from_str(item["avgPrice"].as_str().unwrap()).unwrap(),
                     status: custom_status,
                     order_type: item["type"].as_str().unwrap().parse().unwrap(),
+                    trace_stack: TraceStack::default().on_special("331 binance_swap".to_string()),
                 }
-            }
-            ).collect();
+            }).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
-    async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { todo!() }
+    async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn take_order_symbol(&mut self, _symbol: String, _ct_val: Decimal, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
 
-    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
 
-    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { todo!() }
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
 
-    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { todo!() }
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
 
-    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { todo!() }
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
 
-    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { todo!() }
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
 
-    async fn command_order(&mut self, _order_command: OrderCommand) { todo!() }
+    async fn command_order(&mut self, _order_command: OrderCommand, _trace_stack: TraceStack) { warn!("binance_swap:该交易所方法未实现"); }
 }
 
-pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal) -> Position {
-    let position_mode = match position["positionSide"].as_str().unwrap_or("") {
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
+    let mut position_mode = match position["positionSide"].as_str().unwrap_or("") {
         "BOTH" => PositionModeEnum::Both,
         "LONG" => PositionModeEnum::Long,
         "SHORT" => PositionModeEnum::Short,
-        _ => panic!("Binance:格式化持仓模式错误!\nformat_position_item:position_side={:?}", position["positionSide"])
+        _ => {
+            error!("Binance:格式化持仓模式错误!\nformat_position_item:position_side={:?}", position["positionSide"]);
+            panic!("Binance:格式化持仓模式错误!\nformat_position_item:position_side={:?}", position["positionSide"])
+        }
     };
     let size = Decimal::from_str(position["positionAmt"].as_str().unwrap()).unwrap();
-    let amount = size * amount_size;
+    let amount = size * ct_val;
+    match position_mode {
+        PositionModeEnum::Both => {
+            position_mode = match amount {
+                amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+                amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+                _ => { PositionModeEnum::Both }
+            };
+        }
+        _ => {}
+    }
     Position {
         symbol: position["symbol"].as_str().unwrap_or("").parse().unwrap(),
         margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
         amount,
-        frozen_amount: dec!(0),
+        frozen_amount: Decimal::ZERO,
         price: Decimal::from_str(position["entryPrice"].as_str().unwrap()).unwrap(),
         profit: Decimal::from_str(position["unRealizedProfit"].as_str().unwrap()).unwrap(),
         position_mode,

+ 576 - 0
standard/src/bitget_spot.rs

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

+ 119 - 0
standard/src/bitget_spot_handle.rs

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

+ 15 - 4
standard/src/exchange.rs

@@ -1,12 +1,14 @@
 use std::collections::{BTreeMap};
 use std::io::Error;
 use tokio::sync::mpsc::Sender;
+use crate::{Order, Platform};
 use crate::binance_swap::BinanceSwap;
 use crate::binance_spot::BinanceSpot;
 use crate::gate_spot::GateSpot;
 use crate::gate_swap::GateSwap;
 use crate::kucoin_swap::KucoinSwap;
-use crate::{Order, Platform};
+use crate::bitget_spot::BitgetSpot;
+use crate::okx_swap::OkxSwap;
 
 /// 交易所交易模式枚举
 /// - `BinanceSwap`: Binance交易所期货;
@@ -21,6 +23,8 @@ pub enum ExchangeEnum {
     GateSwap,
     GateSpot,
     KucoinSwap,
+    OkxSwap,
+    BitgetSpot,
 }
 
 /// Exchange结构体
@@ -68,9 +72,6 @@ impl Exchange {
             ExchangeEnum::BinanceSwap => {
                 Box::new(BinanceSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
-            ExchangeEnum::BinanceSpot => {
-                Box::new(BinanceSpot::new(symbol, is_colo, params))
-            }
             ExchangeEnum::GateSwap => {
                 Box::new(GateSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
@@ -80,6 +81,16 @@ impl Exchange {
             ExchangeEnum::KucoinSwap => {
                 Box::new(KucoinSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
+            ExchangeEnum::OkxSwap => {
+                Box::new(OkxSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            ExchangeEnum::BitgetSpot => {
+                Box::new(BitgetSpot::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            _ => {
+                // BinanceSpot
+                Box::new(BinanceSpot::new(symbol, is_colo, params))
+            }
         }
     }
 }

+ 53 - 32
standard/src/gate_handle.rs

@@ -1,8 +1,9 @@
 use std::str::FromStr;
 use rust_decimal::Decimal;
-use rust_decimal_macros::dec;
-use tracing::{debug};
+use rust_decimal::prelude::FromPrimitive;
+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::exchange::ExchangeEnum;
 use crate::handle_info::HandleSwapInfo;
@@ -16,60 +17,76 @@ pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
 }
 
 pub fn format_account_info(data: Vec<serde_json::Value>, symbol: String) -> Account {
+    let symbol_array: Vec<&str> = symbol.split("_").collect();
     let balance_info = data.iter().find(|&item| item["text"].as_str().unwrap().contains(&symbol));
     match balance_info {
         None => {
+            error!("Gate:格式化账号信息错误!\nformat_account_info: balance_info={:?}", balance_info);
             panic!("Gate:格式化账号信息错误!\nformat_account_info: balance_info={:?}", balance_info)
         }
         Some(value) => {
-            let balance = Decimal::from_str(&value["balance"].to_string()).unwrap();
+            let balance = Decimal::from_str(&value["balance"].as_f64().unwrap().to_string()).unwrap();
             Account {
+                coin: symbol_array[1].to_string(),
                 balance,
-                available_balance: dec!(0),
-                frozen_balance: dec!(0),
-                stocks: dec!(0),
-                available_stocks: dec!(0),
-                frozen_stocks: dec!(0),
+                available_balance: Decimal::ZERO,
+                frozen_balance: Decimal::ZERO,
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
             }
         }
     }
 }
 
 // 处理position信息
-pub fn handle_position(res_data: ResponseData, amount_size: Decimal) -> Vec<Position> {
+pub fn handle_position(res_data: ResponseData, ct_val: Decimal) -> Vec<Position> {
     let res_data_str = res_data.data;
     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&res_data_str).unwrap();
-    res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect()
+    res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect()
 }
 
-pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal) -> Position {
-    let position_mode = match position["mode"].as_str().unwrap_or("") {
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
+    let mut position_mode = match position["mode"].as_str().unwrap_or("") {
         "single" => PositionModeEnum::Both,
         "dual_long" => PositionModeEnum::Long,
         "dual_short" => PositionModeEnum::Short,
-        _ => panic!("Gate:格式化持仓模式错误!\nformat_position_item:position_mode={:?}", position["mode"])
+        _ => {
+            error!("Gate:格式化持仓模式错误!\nformat_position_item:position_mode={:?}", position["mode"]);
+            panic!("Gate:格式化持仓模式错误!\nformat_position_item:position_mode={:?}", position["mode"])
+        }
     };
-    let size = Decimal::from_str(&position["size"].to_string()).unwrap();
-    let amount = size * amount_size;
+    let size = Decimal::from_str(&position["size"].as_f64().unwrap().to_string()).unwrap();
+    let amount = size * ct_val;
+    match position_mode {
+        PositionModeEnum::Both => {
+            position_mode = match amount {
+                amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+                amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+                _ => { PositionModeEnum::Both }
+            }
+        }
+        _ => {}
+    }
     Position {
         symbol: position["contract"].as_str().unwrap().to_string(),
-        margin_level: Decimal::from_str(&position["leverage"].to_string()).unwrap(),
+        margin_level: Decimal::from_str(&position["leverage"].as_f64().unwrap().to_string()).unwrap(),
         amount,
-        frozen_amount: dec!(0),
-        price: Decimal::from_str(&position["entry_price"].to_string()).unwrap(),
-        profit: Decimal::from_str(&position["realised_pnl"].to_string()).unwrap(),
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(&position["entry_price"].as_f64().unwrap().to_string()).unwrap(),
+        profit: Decimal::from_str(&position["realised_pnl"].as_f64().unwrap().to_string()).unwrap(),
         position_mode,
-        margin: Decimal::from_str(&position["margin"].to_string()).unwrap(),
+        margin: Decimal::from_str(&position["margin"].as_f64().unwrap().to_string()).unwrap(),
     }
 }
 
 // 处理order信息
-pub fn handle_order(res_data: ResponseData, amount_size: Decimal) -> SpecialOrder {
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
     let res_data_str = res_data.data;
     let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
     let mut order_info = Vec::new();
     for item in res_data_json.iter() {
-        order_info.push(format_order_item(item.clone(), amount_size));
+        order_info.push(format_order_item(item.clone(), ct_val));
     };
 
     SpecialOrder {
@@ -78,26 +95,30 @@ pub fn handle_order(res_data: ResponseData, amount_size: Decimal) -> SpecialOrde
     }
 }
 
-pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Order {
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
     debug!("format-order-start, gate_handle");
     debug!(?order);
     let status = order["status"].as_str().unwrap_or("");
     let text = order["text"].as_str().unwrap_or("");
-    let size = Decimal::from_str(&order["size"].to_string()).unwrap();
-    let left = Decimal::from_str(&order["left"].to_string()).unwrap();
+    let size = Decimal::from_f64(order["size"].as_f64().unwrap()).unwrap();
+    let left = Decimal::from_f64(order["left"].as_f64().unwrap()).unwrap();
 
-    let amount = size * amount_size;
-    let deal_amount = (size - left) * amount_size;
-    let custom_status = if status == "finished" { "REMOVE".to_string() } else if status == "open" { "NEW".to_string() } else { panic!("Gate:格式化订单状态错误!\nformat_order_item:status={:?}", status) };
+    let amount = size * ct_val;
+    let deal_amount = (size - left) * ct_val;
+    let custom_status = if status == "finished" { "REMOVE".to_string() } else if status == "open" { "NEW".to_string() } else {
+        error!("Gate:格式化订单状态错误!\nformat_order_item:status={:?}", status);
+        panic!("Gate:格式化订单状态错误!\nformat_order_item:status={:?}", status)
+    };
     let rst_order = Order {
         id: order["id"].to_string(),
-        custom_id: text.replace("t-my-custom-id_", ""),
-        price: Decimal::from_str(&order["price"].to_string()).unwrap(),
+        custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
+        price: Decimal::from_f64(order["price"].as_f64().unwrap()).unwrap(),
         amount,
         deal_amount,
-        avg_price: Decimal::from_str(&order["fill_price"].to_string()).unwrap(),
+        avg_price: Decimal::from_f64(order["fill_price"].as_f64().unwrap()).unwrap(),
         status: custom_status,
         order_type: "limit".to_string(),
+        trace_stack: TraceStack::default().on_special("120 gate_handle".to_string()),
     };
 
     debug!(?rst_order);
@@ -115,7 +136,7 @@ pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
     for value in value.as_array().unwrap() {
         depth_items.push(MarketOrder {
             price: Decimal::from_str(value["p"].as_str().unwrap()).unwrap(),
-            amount: Decimal::from_str(&value["s"].to_string()).unwrap(),
+            amount: Decimal::from_f64(value["s"].as_f64().unwrap()).unwrap(),
         })
     }
     return depth_items;

+ 48 - 37
standard/src/gate_spot.rs

@@ -1,9 +1,12 @@
 use std::collections::BTreeMap;
-use std::io::{Error};
+use std::io::{Error, ErrorKind};
 use async_trait::async_trait;
 use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use tracing::warn;
 use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand};
 use exchanges::gate_spot_rest::GateSpotRest;
+use global::trace_stack::TraceStack;
 
 #[allow(dead_code)]
 #[derive(Clone)]
@@ -46,57 +49,65 @@ impl Platform for GateSpot {
         self.params.clone()
     }
 
-    fn get_self_market(&self) -> Market { todo!() }
-
-    fn get_request_delays(&self) -> Vec<i64> { todo!() }
-
-    fn get_request_avg_delay(&self) -> Decimal { todo!() }
-
-    fn get_request_max_delay(&self) -> i64 { todo!() }
-
-    async fn get_server_time(&mut self) -> Result<String, Error> {
-        todo!()
+    fn get_self_market(&self) -> Market {
+        warn!("gate_spot:该交易所方法未实现");
+        return Market::new();
     }
 
-    // 获取账号信息
-    async fn get_account(&mut self) -> Result<Account, Error> {
-        todo!()
-    }
-    // 获取仓位信息
-    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
-        todo!()
+    fn get_request_delays(&self) -> Vec<i64> {
+        warn!("gate_spot:该交易所方法未实现");
+        return vec![];
     }
 
-    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
-        todo!()
+    fn get_request_avg_delay(&self) -> Decimal {
+        warn!("gate_spot:该交易所方法未实现");
+        return dec!(0);
     }
 
-    // 获取市场行情
-    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
-        todo!()
+    fn get_request_max_delay(&self) -> i64 {
+        warn!("gate_spot:该交易所方法未实现");
+        return 0;
     }
 
-    async fn get_market(&mut self) -> Result<Market, Error> {
-        todo!()
-    }
+    async fn get_server_time(&mut self) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
+    async fn get_account(&mut self) -> Result<Account, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
-        todo!()
-    }
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_market(&mut self) -> Result<Market, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_market_symbol(&mut self, _symbol: String) -> Result<Market, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn take_order_symbol(&mut self, _symbol: String, _ct_val: Decimal, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
+
+    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { todo!() }
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { todo!() }
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { todo!() }
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { todo!() }
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { todo!() }
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate_spot:该交易所方法未实现".to_string())) }
 
-    async fn command_order(&mut self, _order_command: OrderCommand) { todo!() }
+    async fn command_order(&mut self, _order_command: OrderCommand, _trace_stack: TraceStack) { warn!("gate_spot:该交易所方法未实现"); }
 }

+ 260 - 57
standard/src/gate_swap.rs

@@ -5,13 +5,13 @@ use tokio::sync::mpsc::Sender;
 use async_trait::async_trait;
 use rust_decimal::Decimal;
 use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
-use rust_decimal_macros::dec;
-use serde_json::{json};
+use serde_json::json;
 use futures::stream::FuturesUnordered;
 use futures::{TryStreamExt};
-use tracing::{error, debug};
+use tracing::{error, debug, trace};
 use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, PositionModeEnum};
 use exchanges::gate_swap_rest::GateSwapRest;
+use global::trace_stack::TraceStack;
 
 #[allow(dead_code)]
 #[derive(Clone)]
@@ -39,6 +39,19 @@ impl GateSwap {
             order_sender,
             error_sender,
         };
+
+        // 修改持仓模式
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mode_result = gate_swap.set_dual_mode(symbol_array[1], true).await;
+        match mode_result {
+            Ok(_) => {
+                trace!("Gate:设置持仓模式成功!")
+            }
+            Err(error) => {
+                error!("Gate:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 获取市场信息
         gate_swap.market = GateSwap::get_market(&mut gate_swap).await.unwrap_or(gate_swap.market);
         return gate_swap;
     }
@@ -80,7 +93,7 @@ impl Platform for GateSwap {
             let result = res_data_json["server_time"].to_string();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 获取账号信息
@@ -94,44 +107,49 @@ impl Platform for GateSwap {
             let available_balance = Decimal::from_str(res_data_json["available"].as_str().unwrap()).unwrap();
             let frozen_balance = balance - available_balance;
             let result = Account {
+                coin: symbol_array[1].to_string(),
                 balance,
                 available_balance,
                 frozen_balance,
-                stocks: dec!(0),
-                available_stocks: dec!(0),
-                frozen_stocks: dec!(0),
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "gate_swap:该交易所方法未实现".to_string()))
+    }
+
     // 获取持仓信息
     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_position(symbol_array[1].to_string().to_lowercase(), self.symbol.clone()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let result = res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 获取所有持仓
     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
         let res_data = self.request.get_user_position(symbol_array[1].to_string().to_lowercase()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let result = res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, Decimal::ONE) }).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 获取市场行情
@@ -144,6 +162,37 @@ impl Platform for GateSwap {
             let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == self.symbol);
             match ticker_info {
                 None => {
+                    error!("Gate:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
+                    panic!("Gate:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: chrono::Utc::now().timestamp_millis(),
+                        high: Decimal::from_str(value["high_24h"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low_24h"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["lowest_ask"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["highest_bid"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["volume_24h"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let res_data = self.request.get_ticker(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == symbol);
+            match ticker_info {
+                None => {
+                    error!("Gate:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
                     panic!("Gate:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
                 }
                 Some(value) => {
@@ -160,7 +209,7 @@ impl Platform for GateSwap {
                 }
             }
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -173,15 +222,22 @@ impl Platform for GateSwap {
             let market_info = res_data_json.iter().find(|item| item["name"].as_str().unwrap() == self.symbol);
             match market_info {
                 None => {
+                    error!("Gate:获取Market信息错误!\nget_market:market_info={:?}", market_info);
                     panic!("Gate:获取Market信息错误!\nget_market:market_info={:?}", market_info)
                 }
                 Some(value) => {
                     let name = value["name"].as_str().unwrap();
                     let name_array: Vec<&str> = name.split("_").collect();
                     let tick_size = Decimal::from_str(value["order_price_round"].as_str().unwrap()).unwrap();
-                    let amount_size = Decimal::from_str(value["quanto_multiplier"].as_str().unwrap()).unwrap();
+                    let min_qty = Decimal::from_str(&value["order_size_min"].to_string()).unwrap();
+                    let max_qty = Decimal::from_str(&value["order_size_max"].to_string()).unwrap();
+                    let ct_val = Decimal::from_str(value["quanto_multiplier"].as_str().unwrap()).unwrap();
+
+                    let amount_size = min_qty * ct_val;
                     let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
                     let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+                    let max_notional = max_qty * ct_val;
 
                     let result = Market {
                         symbol: name.to_string(),
@@ -191,60 +247,109 @@ impl Platform for GateSwap {
                         amount_size,
                         price_precision,
                         amount_precision,
-                        min_qty: Decimal::from_str(&value["order_size_min"].to_string()).unwrap(),
-                        max_qty: Decimal::from_str(&value["order_size_max"].to_string()).unwrap(),
-                        min_notional: Default::default(),
-                        max_notional: Default::default(),
-                        ct_val: Default::default(),
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
                     };
                     Ok(result)
                 }
             }
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let res_data = self.request.get_market_details(symbol_array[1].to_string().to_lowercase()).await;
+        if res_data.code == "200" {
+            let res_data_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["name"].as_str().unwrap() == symbol);
+            match market_info {
+                None => {
+                    error!("Gate:获取Market信息错误!\nget_market:market_info={:?}", market_info);
+                    panic!("Gate:获取Market信息错误!\nget_market:market_info={:?}", market_info)
+                }
+                Some(value) => {
+                    let name = value["name"].as_str().unwrap();
+                    let name_array: Vec<&str> = name.split("_").collect();
+                    let tick_size = Decimal::from_str(value["order_price_round"].as_str().unwrap()).unwrap();
+                    let min_qty = Decimal::from_str(&value["order_size_min"].to_string()).unwrap();
+                    let max_qty = Decimal::from_str(&value["order_size_max"].to_string()).unwrap();
+                    let ct_val = Decimal::from_str(value["quanto_multiplier"].as_str().unwrap()).unwrap();
+
+                    let amount_size = min_qty * ct_val;
+                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+                    let max_notional = max_qty * ct_val;
+
+                    let result = Market {
+                        symbol: name.to_string(),
+                        base_asset: name_array[0].to_string(),
+                        quote_asset: name_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
     // 获取订单详情
     async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
-        let id = if order_id.eq("") { format!("t-my-custom-id_{}", custom_id) } else { order_id.to_string() };
+        let ct_val = self.market.ct_val;
+        let id = if order_id.eq("") { format!("t-{}", custom_id) } else { order_id.to_string() };
         let res_data = self.request.get_order_details(symbol_array[1].to_string().to_lowercase(), id).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
-            let result = format_order_item(res_data_json, amount_size);
+            let result = format_order_item(res_data_json, ct_val);
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 获取订单列表
     async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_orders(symbol_array[1].to_string().to_lowercase(), status.to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
             let order_info: Vec<_> = res_data_json.iter().filter(|item| item["contract"].as_str().unwrap_or("") == self.symbol).collect();
-            let result = order_info.iter().map(|&item| format_order_item(item.clone(), amount_size)).collect();
+            let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val)).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 下单接口
     async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let mut params = json!({
-            "text": format!("t-my-custom-id_{}", custom_id),
+            "text": format!("t-{}", custom_id),
             "contract": self.symbol.to_string(),
             "price": price.to_string(),
         });
-        let size = (amount / amount_size).floor();
-        if price.eq(&dec!(0)) {
+        let size = (amount / ct_val).floor();
+        if price.eq(&Decimal::ZERO) {
             params["tif"] = json!("ioc".to_string());
         }
         match origin_side {
@@ -270,42 +375,111 @@ impl Platform for GateSwap {
         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 = format_order_item(res_data_json, amount_size);
+            let result = format_order_item(res_data_json, ct_val);
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
+    async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mut params = json!({
+            "text": format!("t-{}", custom_id),
+            "contract": self.symbol.to_string(),
+            "price": price.to_string(),
+        });
+        let size = (amount / ct_val).floor();
+        if price.eq(&Decimal::ZERO) {
+            params["tif"] = json!("ioc".to_string());
+        }
+        match origin_side {
+            "kd" => {
+                params["reduce_only"] = json!(false);
+                params["size"] = json!(size.to_i64());
+            }
+            "pd" => {
+                params["reduce_only"] = json!(true);
+                params["size"] = serde_json::Value::from((-size).to_i64());
+            }
+            "kk" => {
+                params["reduce_only"] = json!(false);
+                params["size"] = serde_json::Value::from((-size).to_i64());
+            }
+            "pk" => {
+                params["reduce_only"] = json!(true);
+                params["size"] = json!(size.to_i64());
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(symbol_array[1].to_string().to_lowercase(), params).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = format_order_item(res_data_json, ct_val);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
     // 撤销订单
     async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let settle = symbol_array[1].to_string().to_lowercase();
-        let id = if order_id.eq("") { format!("t-my-custom-id_{}", custom_id) } else { order_id.to_string() };
+        let id = if order_id.eq("") { format!("t-{}", custom_id) } else { order_id.to_string() };
         let res_data = self.request.cancel_order(settle, id.to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
-            let result = format_order_item(res_data_json, amount_size);
+            let result = format_order_item(res_data_json, ct_val);
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 批量撤销订单
     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.tick_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.cancel_orders(symbol_array[1].to_string().to_lowercase(), self.symbol.to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
-            let result = res_data_json.iter().map(|item| format_order_item(item.clone(), amount_size)).collect();
+            let result = res_data_json.iter().map(|item| format_order_item(item.clone(), ct_val)).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let ct_val = self.market.ct_val;
+        let orders_res_data = self.request.get_orders(symbol_array[1].to_string().to_lowercase(), "open".to_string()).await;
+        if orders_res_data.code == "200" {
+            let mut result = vec![];
+            let orders_res_data_str = &orders_res_data.data;
+            let orders_res_data_json: Vec<serde_json::Value> = serde_json::from_str(orders_res_data_str).unwrap();
+            for order in orders_res_data_json {
+                let cancel_res_data = self.request.cancel_orders(symbol_array[1].to_string().to_lowercase(), order["contract"].as_str().unwrap().to_string()).await;
+                if cancel_res_data.code == "200" {
+                    let cancel_res_data_str = &cancel_res_data.data;
+                    let cancel_res_data_json: Vec<serde_json::Value> = serde_json::from_str(cancel_res_data_str).unwrap();
+                    for cancel in cancel_res_data_json {
+                        result.push(format_order_item(cancel.clone(), ct_val))
+                    };
+                } else {
+                    return Err(Error::new(ErrorKind::Other, cancel_res_data.to_string()));
+                }
+            }
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, orders_res_data.to_string()))
         }
     }
+
     // 设置持仓模式
     async fn set_dual_mode(&mut self, coin: &str, is_dual_mode: bool) -> Result<String, Error> {
         let coin_format = coin.to_string().to_lowercase();
@@ -315,9 +489,10 @@ impl Platform for GateSwap {
             let result = res_data_str.clone();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
     // 更新双持仓模式下杠杆
     async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
@@ -327,9 +502,12 @@ impl Platform for GateSwap {
             let result = res_data_str.clone();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "gate:该交易所方法未实现".to_string())) }
+
     // 交易账户互转
     async fn wallet_transfers(&mut self, coin: &str, from: &str, to: &str, amount: Decimal) -> Result<String, Error> {
         let coin_format = coin.to_string().to_lowercase();
@@ -339,11 +517,12 @@ impl Platform for GateSwap {
             let result = res_data_str.clone();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
+
     // 指令下单
-    async fn command_order(&mut self, order_command: OrderCommand) {
+    async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
         let mut handles = vec![];
         // 撤销订单
         let cancel = order_command.cancel;
@@ -369,6 +548,7 @@ impl Platform for GateSwap {
                                 result_sd.send(order).await.unwrap();
                             }
                             Err(_) => {
+                                error!("撤单失败,而且查单也失败了,gate_io_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
                                 panic!("撤单失败,而且查单也失败了,gate_io_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
                             }
                         }
@@ -388,6 +568,8 @@ impl Platform for GateSwap {
             let item_clone = item.clone();
             let result_sd = self.order_sender.clone();
             let err_sd = self.error_sender.clone();
+            let mut ts = trace_stack.clone();
+
             let handle = tokio::spawn(async move {
                 let value = limits_clone[&item_clone].clone();
                 let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
@@ -398,7 +580,11 @@ impl Platform for GateSwap {
                 //  order_name: [数量,方向,价格,c_id]
                 let result = self_clone.take_order(cid, side, price, amount).await;
                 match result {
-                    Ok(result) => {
+                    Ok(mut result) => {
+                        // 记录此订单完成时间
+                        ts.on_after_send();
+                        result.trace_stack = ts;
+
                         result_sd.send(result).await.unwrap();
                     }
                     Err(error) => {
@@ -442,20 +628,33 @@ impl Platform for GateSwap {
     }
 }
 
-pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal) -> Position {
-    let position_mode = match position["mode"].as_str().unwrap_or("") {
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
+    let mut position_mode = match position["mode"].as_str().unwrap_or("") {
         "single" => PositionModeEnum::Both,
         "dual_long" => PositionModeEnum::Long,
         "dual_short" => PositionModeEnum::Short,
-        _ => panic!("Gate:格式化持仓模式错误!\nformat_position_item:position_mode={:?}", position["mode"])
+        _ => {
+            error!("Gate:格式化持仓模式错误!\nformat_position_item:position_mode={:?}", position["mode"]);
+            panic!("Gate:格式化持仓模式错误!\nformat_position_item:position_mode={:?}", position["mode"])
+        }
     };
     let size = Decimal::from_str(&position["size"].to_string()).unwrap();
-    let amount = size * amount_size;
+    let amount = size * ct_val;
+    match position_mode {
+        PositionModeEnum::Both => {
+            position_mode = match amount {
+                amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+                amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+                _ => { PositionModeEnum::Both }
+            }
+        }
+        _ => {}
+    }
     Position {
         symbol: position["contract"].as_str().unwrap_or("").parse().unwrap(),
         margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
         amount,
-        frozen_amount: dec!(0),
+        frozen_amount: Decimal::ZERO,
         price: Decimal::from_str(position["entry_price"].as_str().unwrap()).unwrap(),
         profit: Decimal::from_str(position["unrealised_pnl"].as_str().unwrap()).unwrap(),
         position_mode,
@@ -463,7 +662,7 @@ pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal)
     }
 }
 
-pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Order {
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
     debug!("format-order-start, gate_swap");
     debug!(?order);
     let status = order["status"].as_str().unwrap_or("");
@@ -471,18 +670,22 @@ pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Orde
     let size = Decimal::from_str(&order["size"].to_string()).unwrap();
     let left = Decimal::from_str(&order["left"].to_string()).unwrap();
 
-    let amount = size * amount_size;
-    let deal_amount = (size - left) * amount_size;
-    let custom_status = if status == "finished" { "REMOVE".to_string() } else if status == "open" { "NEW".to_string() } else { panic!("Gate:格式化订单状态错误!\nformat_order_item:status={:?}", status) };
+    let amount = size * ct_val;
+    let deal_amount = (size - left) * ct_val;
+    let custom_status = if status == "finished" { "REMOVE".to_string() } else if status == "open" { "NEW".to_string() } else {
+        error!("Gate:格式化订单状态错误!\nformat_order_item:status={:?}", status);
+        panic!("Gate:格式化订单状态错误!\nformat_order_item:status={:?}", status)
+    };
     let rst_order = Order {
         id: order["id"].to_string(),
-        custom_id: text.replace("t-my-custom-id_", ""),
+        custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
         price: Decimal::from_str(order["price"].as_str().unwrap()).unwrap(),
         amount,
         deal_amount,
         avg_price: Decimal::from_str(&order["fill_price"].as_str().unwrap()).unwrap(),
         status: custom_status,
         order_type: "limit".to_string(),
+        trace_stack: TraceStack::default().on_special("688 trace_stack".to_string()),
     };
     debug!(?rst_order);
     debug!("format-order-end, gate_swap");

+ 61 - 18
standard/src/handle_info.rs

@@ -2,10 +2,11 @@ use std::cmp::Ordering;
 use rust_decimal::{Decimal};
 use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
+use tracing::error;
 use exchanges::response_base::ResponseData;
 use global::public_params;
 use crate::exchange::ExchangeEnum;
-use crate::{Account, binance_handle, gate_handle, kucoin_handle, MarketOrder, Position, SpecialDepth, SpecialOrder, SpecialTicker};
+use crate::{Account, binance_handle, bitget_spot_handle, gate_handle, kucoin_handle, MarketOrder, okx_handle, Position, SpecialDepth, SpecialOrder, SpecialTicker};
 
 #[allow(dead_code)]
 pub struct HandleSwapInfo;
@@ -16,7 +17,8 @@ impl HandleSwapInfo {
     pub fn handle_account_info(exchange: ExchangeEnum, res_data: ResponseData, symbol: String) -> Account {
         match exchange {
             ExchangeEnum::BinanceSwap => {
-                panic!("暂未提供此交易所方法!")
+                error!("暂未提供此交易所方法!handle_account_info:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_account_info:{:?}", exchange);
             }
             ExchangeEnum::GateSwap => {
                 gate_handle::handle_account_info(res_data, symbol)
@@ -24,8 +26,15 @@ impl HandleSwapInfo {
             ExchangeEnum::KucoinSwap => {
                 kucoin_handle::handle_account_info(res_data, symbol)
             }
+            ExchangeEnum::OkxSwap => {
+                okx_handle::handle_account_info(res_data, symbol)
+            }
+            ExchangeEnum::BitgetSpot => {
+                bitget_spot_handle::handle_account_info(res_data, symbol)
+            }
             _ => {
-                panic!("参数错误!")
+                error!("参数错误!handle_account_info: {:?}",exchange);
+                panic!("参数错误!handle_account_info: {:?}", exchange);
             }
         }
     }
@@ -36,47 +45,72 @@ impl HandleSwapInfo {
                 binance_handle::handle_special_ticker(res_data)
             }
             ExchangeEnum::GateSwap => {
-                panic!("暂未提供此交易所方法!")
+                error!("暂未提供此交易所方法!handle_special_ticker:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_special_ticker:{:?}", exchange);
             }
             ExchangeEnum::KucoinSwap => {
                 kucoin_handle::handle_special_ticker(res_data)
             }
+            ExchangeEnum::OkxSwap => {
+                okx_handle::handle_special_ticker(res_data)
+            }
+            ExchangeEnum::BitgetSpot => {
+                bitget_spot_handle::handle_special_ticker(res_data)
+            }
             _ => {
-                panic!("参数错误!")
+                error!("参数错误!handle_special_ticker: {:?}",exchange);
+                panic!("参数错误!handle_special_ticker: {:?}", exchange);
             }
         }
     }
     // 处理position信息
-    pub fn handle_position(exchange: ExchangeEnum, res_data: ResponseData, amount_size: Decimal) -> Vec<Position> {
+    pub fn handle_position(exchange: ExchangeEnum, res_data: ResponseData, ct_val: Decimal) -> Vec<Position> {
         match exchange {
             ExchangeEnum::BinanceSwap => {
-                panic!("暂未提供此交易所方法!")
+                error!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_position:{:?}", exchange);
             }
             ExchangeEnum::GateSwap => {
-                gate_handle::handle_position(res_data, amount_size)
+                gate_handle::handle_position(res_data, ct_val)
             }
             ExchangeEnum::KucoinSwap => {
-                kucoin_handle::handle_position(res_data, amount_size)
+                kucoin_handle::handle_position(res_data, ct_val)
+            }
+            ExchangeEnum::OkxSwap => {
+                okx_handle::handle_position(res_data, ct_val)
+            }
+            ExchangeEnum::BitgetSpot => {
+                error!("暂未提供此交易所方法!handle_position:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_position:{:?}", exchange);
             }
             _ => {
-                panic!("参数错误!")
+                error!("参数错误!handle_position: {:?}",exchange);
+                panic!("参数错误!handle_position: {:?}", exchange);
             }
         }
     }
     // 处理订单信息
-    pub fn handle_order(exchange: ExchangeEnum, res_data: ResponseData, amount_size: Decimal) -> SpecialOrder {
+    pub fn handle_order(exchange: ExchangeEnum, res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
         match exchange {
             ExchangeEnum::BinanceSwap => {
-                panic!("暂未提供此交易所方法!")
+                error!("暂未提供此交易所方法!handle_order:{:?}", exchange);
+                panic!("暂未提供此交易所方法!handle_order:{:?}", exchange);
             }
             ExchangeEnum::GateSwap => {
-                gate_handle::handle_order(res_data, amount_size)
+                gate_handle::handle_order(res_data, ct_val)
             }
             ExchangeEnum::KucoinSwap => {
-                kucoin_handle::handle_order(res_data, amount_size)
+                kucoin_handle::handle_order(res_data, ct_val)
+            }
+            ExchangeEnum::OkxSwap => {
+                okx_handle::handle_order(res_data, ct_val)
+            }
+            ExchangeEnum::BitgetSpot => {
+                bitget_spot_handle::handle_order(res_data, ct_val)
             }
             _ => {
-                panic!("参数错误!")
+                error!("参数错误!handle_order: {:?}",exchange);
+                panic!("参数错误!handle_order: {:?}", exchange);
             }
         }
     }
@@ -99,8 +133,17 @@ impl HandleSwapInfo {
                 depth_asks = kucoin_handle::format_depth_items(res_data_json["asks"].clone());
                 depth_bids = kucoin_handle::format_depth_items(res_data_json["bids"].clone());
             }
+            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());
+            }
+            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());
+            }
             _ => {
-                panic!("参数错误!")
+                error!("参数错误!handle_special_depth: {:?}",exchange);
+                panic!("参数错误!handle_special_depth: {:?}", exchange);
             }
         }
         depth_asks.sort_by(|a, b| a.price.partial_cmp(&b.price).unwrap_or(Ordering::Equal));
@@ -124,7 +167,7 @@ impl HandleSwapInfo {
         for item in depth_asks.iter() {
             let price = item.price;
             let amount = item.amount;
-            if av.get(ap_index).is_none() { av.push(dec!(0)) };
+            if av.get(ap_index).is_none() { av.push(Decimal::ZERO) };
             if price < ap_price_tag {
                 av[ap_index] += amount;
             } else {
@@ -142,7 +185,7 @@ impl HandleSwapInfo {
         for item in depth_bids.iter() {
             let price = item.price;
             let amount = item.amount;
-            if bv.get(bp_index).is_none() { bv.push(dec!(0)) };
+            if bv.get(bp_index).is_none() { bv.push(Decimal::ZERO) };
             if price > bp_price_tag {
                 bv[bp_index] += amount;
             } else {

+ 38 - 29
standard/src/kucoin_handle.rs

@@ -1,29 +1,33 @@
 use std::str::FromStr;
 use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
 use rust_decimal_macros::dec;
 use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
 use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
 use crate::exchange::ExchangeEnum;
 use crate::handle_info::HandleSwapInfo;
 
 // 处理账号信息
-pub fn handle_account_info(res_data: ResponseData, _symbol: String) -> Account {
+pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
     let res_data_str = res_data.data;
     let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
-    format_account_info(res_data_json)
+    format_account_info(res_data_json, symbol)
 }
 
-pub fn format_account_info(data: serde_json::Value) -> Account {
+pub fn format_account_info(data: serde_json::Value, symbol: String) -> Account {
+    let symbol_array: Vec<&str> = symbol.split("_").collect();
     let available_balance = Decimal::from_str(data["availableBalance"].as_str().unwrap()).unwrap();
     let frozen_balance = Decimal::from_str(data["holdBalance"].as_str().unwrap()).unwrap();
     let balance = available_balance + frozen_balance;
     Account {
+        coin: symbol_array[1].to_string(),
         balance,
         available_balance,
         frozen_balance,
-        stocks: dec!(0),
-        available_stocks: dec!(0),
-        frozen_stocks: dec!(0),
+        stocks: Decimal::ZERO,
+        available_stocks: Decimal::ZERO,
+        frozen_stocks: Decimal::ZERO,
     }
 }
 
@@ -36,9 +40,9 @@ pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
 
 pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
     let bp = Decimal::from_str(&data["bestBidPrice"].as_str().unwrap()).unwrap();
-    let bq = Decimal::from_str(&data["bestBidSize"].to_string()).unwrap();
+    let bq = Decimal::from_f64(data["bestBidSize"].as_f64().unwrap()).unwrap();
     let ap = Decimal::from_str(&data["bestAskPrice"].as_str().unwrap()).unwrap();
-    let aq = Decimal::from_str(&data["bestAskSize"].to_string()).unwrap();
+    let aq = Decimal::from_f64(data["bestAskSize"].as_f64().unwrap()).unwrap();
     let mp = (bp + ap) * dec!(0.5);
 
     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
@@ -51,30 +55,34 @@ pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialD
 }
 
 // 处理position信息
-pub fn handle_position(res_data: ResponseData, amount_size: Decimal) -> Vec<Position> {
+pub fn handle_position(res_data: ResponseData, ct_val: Decimal) -> Vec<Position> {
     let res_data_str = res_data.data;
     let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
-    let result = format_position_item(&res_data_json, amount_size);
+    let result = format_position_item(&res_data_json, ct_val);
     return vec![result];
 }
 
-pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal) -> Position {
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
     let symbol = position["symbol"].as_str().unwrap_or("");
-    let real_leverage = Decimal::from_str(&position["realLeverage"].to_string()).unwrap();
+    let real_leverage = Decimal::from_f64(position["realLeverage"].as_f64().unwrap()).unwrap();
     let currency = position["settleCurrency"].as_str().unwrap_or("");
     let coin = &symbol[..symbol.find(currency).unwrap_or(0)];
-    let avg_entry_price = Decimal::from_str(&position["avgEntryPrice"].to_string()).unwrap();
-    let unrealised_pnl = Decimal::from_str(&position["unrealisedPnl"].to_string()).unwrap();
-    let pos_margin = Decimal::from_str(&position["posMargin"].to_string()).unwrap();
+    let avg_entry_price = Decimal::from_f64(position["avgEntryPrice"].as_f64().unwrap()).unwrap();
+    let unrealised_pnl = Decimal::from_f64(position["unrealisedPnl"].as_f64().unwrap()).unwrap();
+    let pos_margin = Decimal::from_f64(position["posMargin"].as_f64().unwrap()).unwrap();
 
-    let current_qty = Decimal::from_str(&position["currentQty"].to_string()).unwrap();
-    let amount = current_qty * amount_size;
-    let position_mode = if amount > dec!(0) { PositionModeEnum::Long } else { PositionModeEnum::Short };
+    let current_qty = Decimal::from_f64(position["currentQty"].as_f64().unwrap()).unwrap();
+    let amount = current_qty * ct_val;
+    let position_mode = match amount {
+        amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+        amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+        _ => { PositionModeEnum::Both }
+    };
     Position {
         symbol: format!("{}_{}", coin, currency),
         margin_level: real_leverage,
         amount,
-        frozen_amount: dec!(0),
+        frozen_amount: Decimal::ZERO,
         price: avg_entry_price,
         profit: unrealised_pnl,
         position_mode,
@@ -83,12 +91,12 @@ pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal)
 }
 
 // 处理order信息
-pub fn handle_order(res_data: ResponseData, amount_size: Decimal) -> SpecialOrder {
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
     let res_data_str = res_data.data;
     let res_data_json: Vec<serde_json::Value> = vec![serde_json::from_str(&*res_data_str).unwrap()];
     let mut order_info = Vec::new();
     for item in res_data_json.iter() {
-        order_info.push(format_order_item(item.clone(), amount_size));
+        order_info.push(format_order_item(item.clone(), ct_val));
     }
     SpecialOrder {
         name: res_data.label,
@@ -97,7 +105,7 @@ pub fn handle_order(res_data: ResponseData, amount_size: Decimal) -> SpecialOrde
 }
 
 // ws处理
-pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Order {
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
     let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
     let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
     let status = order["status"].as_str().unwrap_or("");
@@ -106,15 +114,15 @@ pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Orde
 
     let avg_price = price;
 
-    let amount = size * amount_size;
-    let deal_amount = filled_size * amount_size;
+    let amount = size * ct_val;
+    let deal_amount = filled_size * ct_val;
     let custom_status;
-     if ["filled", "canceled"].contains(&type_) {
-         custom_status = "REMOVE".to_string();
+    if ["filled", "canceled"].contains(&type_) {
+        custom_status = "REMOVE".to_string();
     } else if ["open"].contains(&status) {
-         custom_status = "NEW".to_string();
+        custom_status = "NEW".to_string();
     } else {
-         custom_status = "NULL".to_string();
+        custom_status = "NULL".to_string();
     }
     Order {
         id: order["orderId"].as_str().unwrap().to_string(),
@@ -125,6 +133,7 @@ pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Orde
         avg_price,
         status: custom_status,
         order_type: order["type"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::default().on_special("136 kucoin_handle".to_string()),
     }
 }
 
@@ -138,7 +147,7 @@ pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
     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].to_string()).unwrap(),
+            amount: Decimal::from_f64(value[1].as_f64().unwrap()).unwrap(),
         })
     }
     return depth_items;

+ 253 - 67
standard/src/kucoin_swap.rs

@@ -8,9 +8,10 @@ use futures::TryStreamExt;
 use rust_decimal::Decimal;
 use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
 use rust_decimal_macros::dec;
-use serde_json::{json, Value};
-use tracing::{error};
+use serde_json::json;
+use tracing::error;
 use exchanges::kucoin_swap_rest::KucoinSwapRest;
+use global::trace_stack::TraceStack;
 use crate::exchange::ExchangeEnum;
 use crate::{Account, kucoin_handle, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
 
@@ -41,6 +42,9 @@ impl KucoinSwap {
             error_sender,
         };
         kucoin_swap.market = KucoinSwap::get_market(&mut kucoin_swap).await.unwrap_or(kucoin_swap.market);
+
+        // 开启自动追加保证金
+        kucoin_swap.set_auto_deposit_status(true).await.unwrap();
         return kucoin_swap;
     }
 }
@@ -78,7 +82,7 @@ impl Platform for KucoinSwap {
             let result = res_data_str.clone();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
     // 获取账号信息
@@ -88,51 +92,57 @@ impl Platform for KucoinSwap {
         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 balance = Decimal::from_str(&res_data_json["accountEquity"].to_string()).unwrap();
-            let available_balance = Decimal::from_str(&res_data_json["availableBalance"].to_string()).unwrap();
+
+            let balance = Decimal::from_f64(res_data_json["marginBalance"].as_f64().unwrap()).unwrap();
+            let available_balance = Decimal::from_f64(res_data_json["availableBalance"].as_f64().unwrap()).unwrap();
             let frozen_balance = balance - available_balance;
             let result = Account {
+                coin: symbol_array[1].to_string(),
                 balance,
                 available_balance,
                 frozen_balance,
-                stocks: dec!(0),
-                available_stocks: dec!(0),
-                frozen_stocks: dec!(0),
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
     async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
         let symbol_format = format!("{}M", utils::format_symbol(self.symbol.clone(), ""));
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_position(symbol_format).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
-            let result = kucoin_handle::format_position_item(&res_data_json, amount_size);
+            let result = kucoin_handle::format_position_item(&res_data_json, ct_val);
             Ok(vec![result])
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
     async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
         let symbol_array: Vec<&str> = self.symbol.split("_").collect();
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_positions(symbol_array[1].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 mut result = Vec::new();
             for item in res_data_json.iter() {
-                result.push(kucoin_handle::format_position_item(item, amount_size))
+                result.push(kucoin_handle::format_position_item(item, ct_val))
             }
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -146,16 +156,39 @@ impl Platform for KucoinSwap {
             let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
             let result = Ticker {
                 time,
-                high: Decimal::from_str(&ticker_info["bestAskPrice"].to_string()).unwrap(),
-                low: Decimal::from_str(&ticker_info["bestBidPrice"].to_string()).unwrap(),
-                sell: Decimal::from_str(&ticker_info["bestAskPrice"].to_string()).unwrap(),
-                buy: Decimal::from_str(&ticker_info["bestBidPrice"].to_string()).unwrap(),
-                last: Decimal::from_str(&ticker_info["price"].to_string()).unwrap(),
-                volume: Decimal::from_str(&ticker_info["size"].to_string()).unwrap(),
+                high: Decimal::from_f64(ticker_info["bestAskPrice"].as_f64().unwrap()).unwrap(),
+                low: Decimal::from_f64(ticker_info["bestBidPrice"].as_f64().unwrap()).unwrap(),
+                sell: Decimal::from_f64(ticker_info["bestAskPrice"].as_f64().unwrap()).unwrap(),
+                buy: Decimal::from_f64(ticker_info["bestBidPrice"].as_f64().unwrap()).unwrap(),
+                last: Decimal::from_f64(ticker_info["price"].as_f64().unwrap()).unwrap(),
+                volume: Decimal::from_f64(ticker_info["size"].as_f64().unwrap()).unwrap(),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        let symbol_format = format!("{}M", utils::format_symbol(symbol.clone(), ""));
+        let res_data = self.request.get_ticker(symbol_format).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let ticker_info = res_data_json;
+            let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
+            let result = Ticker {
+                time,
+                high: Decimal::from_f64(ticker_info["bestAskPrice"].as_f64().unwrap()).unwrap(),
+                low: Decimal::from_f64(ticker_info["bestBidPrice"].as_f64().unwrap()).unwrap(),
+                sell: Decimal::from_f64(ticker_info["bestAskPrice"].as_f64().unwrap()).unwrap(),
+                buy: Decimal::from_f64(ticker_info["bestBidPrice"].as_f64().unwrap()).unwrap(),
+                last: Decimal::from_f64(ticker_info["price"].as_f64().unwrap()).unwrap(),
+                volume: Decimal::from_f64(ticker_info["size"].as_f64().unwrap()).unwrap(),
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -168,18 +201,20 @@ impl Platform for KucoinSwap {
             let market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
             match market_info {
                 None => {
+                    error!("Kucoin:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
                     panic!("Kucoin:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
                 }
                 Some(value) => {
                     let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
                     let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
-                    let tick_size = Decimal::from_str(&value["tickSize"].to_string()).unwrap();
-                    let amount_size = Decimal::from_str(&value["multiplier"].to_string()).unwrap();
-                    let min_qty = Decimal::from_str(&value["lotSize"].to_string()).unwrap();
+                    let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
+                    let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
+                    let ct_val = Decimal::from_f64(value["multiplier"].as_f64().unwrap()).unwrap();
 
+                    let amount_size = min_qty * ct_val;
                     let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
-                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
-                    let min_notional = min_qty * amount_size;
+                    let amount_precision = Decimal::from_u32(ct_val.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
 
                     let result = Market {
                         symbol: format!("{}_{}", base_asset, quote_asset),
@@ -190,51 +225,147 @@ impl Platform for KucoinSwap {
                         price_precision,
                         amount_precision,
                         min_qty,
-                        max_qty: Decimal::from_str(&value["maxOrderQty"].to_string()).unwrap(),
+                        max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
                         min_notional,
-                        max_notional: Decimal::from_str(&value["maxPrice"].to_string()).unwrap(),
-                        ct_val: Default::default(),
+                        max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
+                        ct_val,
                     };
                     Ok(result)
                 }
             }
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_format = format!("{}M", utils::format_symbol(symbol.clone(), ""));
+        let res_data = self.request.get_market_details().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("Kucoin:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
+                    panic!("Kucoin:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
+                }
+                Some(value) => {
+                    let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
+                    let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
+                    let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
+                    let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
+                    let ct_val = Decimal::from_f64(value["multiplier"].as_f64().unwrap()).unwrap();
+
+                    let amount_size = min_qty * ct_val;
+                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+                    let amount_precision = Decimal::from_u32(ct_val.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset, quote_asset),
+                        base_asset,
+                        quote_asset,
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
+                        min_notional,
+                        max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
     async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_orders_details(order_id.to_string(), custom_id.to_string()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
-            let result = format_order_item(res_data_json, amount_size);
+            let result = format_order_item(res_data_json, ct_val);
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
     async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error> {
         let symbol_format = format!("{}M", utils::format_symbol(self.symbol.clone(), ""));
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
         let res_data = self.request.get_orders(status.to_string(), symbol_format.clone()).await;
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
             let order_list: Vec<serde_json::Value> = res_data_json["items"].as_array().unwrap().clone();
             let order_info: Vec<&serde_json::Value> = order_list.iter().filter(|item| item["symbol"].as_str().unwrap_or("") == symbol_format.clone()).collect();
-            let result = order_info.iter().map(|&item| format_order_item(item.clone(), amount_size)).collect();
+            let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val)).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
     async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
         let symbol_format = format!("{}M", utils::format_symbol(self.symbol.clone(), ""));
-        let amount_size = self.market.amount_size;
+        let ct_val = self.market.ct_val;
+        let mut params = json!({
+            "clientOid": custom_id,
+            "symbol": symbol_format,
+            "leverage": "10",
+            "reduceOnly":false,
+            "price": price.to_string(),
+        });
+        params["type"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
+        let size = (amount / ct_val).floor();
+        params["size"] = json!(size);
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+            }
+            "pd" => {
+                params["side"] = json!("sell");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+            }
+            "pk" => {
+                params["side"] = json!("buy");
+            }
+            _ => { error!("下单参数错误"); }
+        };
+
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let id = res_data_json["orderId"].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id: custom_id.to_string(),
+                price,
+                amount,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("359 kucoin_swap".to_string()),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = format!("{}M", utils::format_symbol(symbol.clone(), ""));
         let mut params = json!({
             "clientOid": custom_id,
             "symbol": symbol_format,
@@ -242,7 +373,7 @@ impl Platform for KucoinSwap {
             "reduceOnly":false,
             "price": price.to_string(),
         });
-        let size = (amount / amount_size).floor();
+        let size = (amount / ct_val).floor();
         params["size"] = json!(size);
         match origin_side {
             "kd" => {
@@ -270,20 +401,24 @@ impl Platform for KucoinSwap {
                 custom_id: custom_id.to_string(),
                 price,
                 amount,
-                deal_amount: dec!(0),
-                avg_price: dec!(0),
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
                 status: "NEW".to_string(),
                 order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("408 kucoin_swap".to_string()),
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
     async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
         let res_data = self.request.cancel_order(order_id.to_string(), custom_id.to_string()).await;
-        if order_id == "" { panic!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id) }
+        if order_id == "" {
+            error!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id);
+            panic!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id)
+        }
         if res_data.code == "200" {
             let res_data_str = &res_data.data;
             let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
@@ -292,16 +427,17 @@ impl Platform for KucoinSwap {
             let result = Order {
                 id,
                 custom_id: custom_id.to_string(),
-                price: dec!(0),
-                amount: dec!(0),
-                deal_amount: dec!(0),
-                avg_price: dec!(0),
+                price: Decimal::ZERO,
+                amount: Decimal::ZERO,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
                 status: "REMOVE".to_string(),
                 order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("436 kucoin_swap".to_string()),
             };
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
@@ -316,28 +452,72 @@ impl Platform for KucoinSwap {
                 Order {
                     id: item.as_str().unwrap().to_string(),
                     custom_id: "".to_string(),
-                    price: dec!(0),
-                    amount: dec!(0),
-                    deal_amount: dec!(0),
-                    avg_price: dec!(0),
+                    price: Decimal::ZERO,
+                    amount: Decimal::ZERO,
+                    deal_amount: Decimal::ZERO,
+                    avg_price: Decimal::ZERO,
                     status: "REMOVE".to_string(),
                     order_type: "".to_string(),
+                    trace_stack: TraceStack::default().on_special("461 kucoin_swap".to_string()),
                 }
             ).collect();
             Ok(result)
         } else {
-            Err(Error::new(ErrorKind::Other, res_data.message))
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
         }
     }
 
-    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { panic!("Kucoin:该交易所暂不支持此方法!"); }
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        let res_data = self.request.cancel_order_all().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
+            let result = cancel_ids.iter().map(|item|
+                Order {
+                    id: item.as_str().unwrap().to_string(),
+                    custom_id: "".to_string(),
+                    price: Decimal::ZERO,
+                    amount: Decimal::ZERO,
+                    deal_amount: Decimal::ZERO,
+                    avg_price: Decimal::ZERO,
+                    status: "REMOVE".to_string(),
+                    order_type: "".to_string(),
+                    trace_stack: TraceStack::default().on_special("486 kucoin_swap".to_string()),
+                }
+            ).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
 
-    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { panic!("Kucoin:该交易所暂不支持此方法!"); }
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
 
-    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { panic!("Kucoin:该交易所暂不支持此方法!"); }
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_auto_deposit_status(&mut self, status: bool) -> Result<String, Error> {
+        let symbol_format = format!("{}M", utils::format_symbol(self.symbol.clone(), ""));
+        let res_data = self.request.auto_deposit_status(symbol_format, status).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
+    }
 
     // 指令下单
-    async fn command_order(&mut self, order_command: OrderCommand) {
+    async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
         let mut handles = vec![];
         // 撤销订单
         let cancel = order_command.cancel;
@@ -363,9 +543,9 @@ impl Platform for KucoinSwap {
                                 Ok(order) => {
                                     result_sd.send(order).await.unwrap();
                                 }
-                                Err(query_err) => {
-                                    error!(?query_err);
-                                    error!("撤单失败,而且查单也失败了,kucoin_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+                                Err(_query_err) => {
+                                    // error!(?_query_err);
+                                    // error!("撤单失败,而且查单也失败了,kucoin_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
                                 }
                             }
                             err_sd.send(error).await.unwrap();
@@ -385,6 +565,8 @@ impl Platform for KucoinSwap {
             let item_clone = item.clone();
             let result_sd = self.order_sender.clone();
             let err_sd = self.error_sender.clone();
+            let mut ts = trace_stack.clone();
+
             let handle = tokio::spawn(async move {
                 let value = limits_clone[&item_clone].clone();
                 let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
@@ -395,7 +577,10 @@ impl Platform for KucoinSwap {
                 //  order_name: [数量,方向,价格,c_id]
                 let result = self_clone.take_order(cid, side, price, amount).await;
                 match result {
-                    Ok(result) => {
+                    Ok(mut result) => {
+                        ts.on_after_send();
+                        result.trace_stack = ts.clone();
+
                         result_sd.send(result).await.unwrap();
                     }
                     Err(error) => {
@@ -439,16 +624,16 @@ impl Platform for KucoinSwap {
     }
 }
 
-pub fn format_order_item(order: Value, amount_size: Decimal) -> Order {
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
     let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
-    let size = Decimal::from_str(&order["size"].to_string()).unwrap();
+    let size = Decimal::from_f64(order["size"].as_f64().unwrap()).unwrap();
     let status = order["status"].as_str().unwrap_or("");
-    let filled_size = Decimal::from_str(&order["filledSize"].to_string()).unwrap();
+    let filled_size = Decimal::from_f64(order["filledSize"].as_f64().unwrap()).unwrap();
     let filled_value = Decimal::from_str(order["filledValue"].as_str().unwrap()).unwrap();
 
-    let amount = size * amount_size;
-    let deal_amount = filled_size * amount_size;
-    let avg_price = if deal_amount.is_zero() { dec!(0) } else { filled_value / deal_amount };
+    let amount = size * ct_val;
+    let deal_amount = filled_size * ct_val;
+    let avg_price = if deal_amount.is_zero() { Decimal::ZERO } else { filled_value / deal_amount };
     let custom_status;
     if ["cancelled", "closed", "finished"].contains(&status) {
         custom_status = "REMOVE".to_string();
@@ -467,5 +652,6 @@ pub fn format_order_item(order: Value, amount_size: Decimal) -> Order {
         avg_price,
         status: custom_status,
         order_type: order["type"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::default().on_special("655 kucoin_swap".to_string()),
     }
 }

+ 38 - 3
standard/src/lib.rs

@@ -1,7 +1,10 @@
 use std::collections::{BTreeMap, HashMap};
+use std::fmt;
+use std::fmt::Formatter;
 use std::io::{Error};
 use async_trait::async_trait;
 use rust_decimal::Decimal;
+use global::trace_stack::TraceStack;
 
 use crate::exchange::ExchangeEnum;
 
@@ -9,6 +12,7 @@ use crate::exchange::ExchangeEnum;
 pub mod utils;
 // 引入exchange模块
 pub mod exchange;
+pub mod handle_info;
 // 引入binance模块
 mod binance_swap;
 mod binance_spot;
@@ -19,7 +23,10 @@ mod gate_spot;
 pub mod gate_handle;
 mod kucoin_swap;
 pub mod kucoin_handle;
-pub mod handle_info;
+mod okx_swap;
+pub mod okx_handle;
+mod bitget_spot;
+pub mod bitget_spot_handle;
 
 /// 持仓模式枚举
 /// - `Both`:单持仓方向
@@ -64,7 +71,19 @@ impl OrderCommand {
     }
 }
 
+impl fmt::Display for OrderCommand {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "订单触发数据: cancel: {:?}, check: {:?}, limits_open: {:?}, limits_close: {:?}",
+               self.cancel,
+               self.check,
+               self.limits_open,
+               self.limits_close
+        )
+    }
+}
+
 /// Account结构体(账户信息)
+/// - `coin(String)`: 货币;
 /// - `balance(Decimal)`: 总计计价币数量;
 /// - `available_balance(Decimal)`: 可用计价币数量;
 /// - `frozen_balance(Decimal)`: balance挂单的冻结数量
@@ -73,6 +92,7 @@ impl OrderCommand {
 /// - `frozen_stocks(Decimal)`: stocks挂单的冻结数量
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Account {
+    pub coin: String,
     pub balance: Decimal,
     pub available_balance: Decimal,
     pub frozen_balance: Decimal,
@@ -84,6 +104,7 @@ pub struct Account {
 impl Account {
     pub fn new() -> Account {
         Account {
+            coin: "".to_string(),
             balance: Default::default(),
             available_balance: Default::default(),
             frozen_balance: Default::default(),
@@ -242,6 +263,7 @@ pub struct Order {
     pub avg_price: Decimal,
     pub status: String,
     pub order_type: String,
+    pub trace_stack: TraceStack,
 }
 
 impl Order {
@@ -255,6 +277,7 @@ impl Order {
             avg_price: Default::default(),
             status: "".to_string(),
             order_type: "".to_string(),
+            trace_stack: TraceStack::default(),
         }
     }
 }
@@ -482,30 +505,42 @@ pub trait Platform {
     async fn get_server_time(&mut self) -> Result<String, Error>;
     // 获取账号信息
     async fn get_account(&mut self) -> Result<Account, Error>;
+    // 获取现货账号信息
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error>;
     // 获取持仓信息
     async fn get_position(&mut self) -> Result<Vec<Position>, Error>;
     // 获取所有持仓
     async fn get_positions(&mut self) -> Result<Vec<Position>, Error>;
     // 获取市场行情
     async fn get_ticker(&mut self) -> Result<Ticker, Error>;
-    // 查询所有的合约信息
+    // 获取市场行情自定义交易对
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error>;
+    // 查询所有的市场信息
     async fn get_market(&mut self) -> Result<Market, Error>;
+    // 查询所有的市场信息自定义交易对
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error>;
     // 查询订单详情
     async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error>;
     // 获取订单列表
     async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error>;
     // 下单接口
     async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error>;
+    // 下单接口自定义交易对
+    async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error>;
     // 撤销订单
     async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error>;
     // 批量撤销订单
     async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error>;
+    // 撤销所有订单
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error>;
     // 设置持仓模式
     async fn set_dual_mode(&mut self, coin: &str, is_dual_mode: bool) -> Result<String, Error>;
     // 更新双持仓模式下杠杆
     async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error>;
+    // 设置自动追加保证金
+    async fn set_auto_deposit_status(&mut self, status: bool) -> Result<String, Error>;
     // 交易账户互转
     async fn wallet_transfers(&mut self, coin: &str, from: &str, to: &str, amount: Decimal) -> Result<String, Error>;
     // 指令下单
-    async fn command_order(&mut self, order_command: OrderCommand);
+    async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack);
 }

+ 158 - 0
standard/src/okx_handle.rs

@@ -0,0 +1,158 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use tracing::trace;
+use exchanges::response_base::ResponseData;
+use global::trace_stack::TraceStack;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
+use crate::exchange::ExchangeEnum;
+use crate::handle_info::HandleSwapInfo;
+
+// 处理账号信息
+pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
+    let res_data_str = res_data.data;
+    let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+    let account_info = res_data_json[0]["details"].clone();
+    let details = account_info[0].clone();
+    format_account_info(details, symbol)
+}
+
+pub fn format_account_info(data: serde_json::Value, symbol: String) -> Account {
+    let symbol_array: Vec<&str> = symbol.split("_").collect();
+    Account {
+        coin: symbol_array[1].to_string(),
+        balance: Decimal::from_str(data["cashBal"].as_str().unwrap()).unwrap(),
+        available_balance: Decimal::from_str(data["availBal"].as_str().unwrap()).unwrap(),
+        frozen_balance: Decimal::from_str(data["fixedBal"].as_str().unwrap()).unwrap(),
+        stocks: Decimal::ZERO,
+        available_stocks: Decimal::ZERO,
+        frozen_stocks: Decimal::ZERO,
+    }
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
+    let res_data_str = res_data.data;
+    let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+    trace!(?res_data_json);
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), ct_val));
+    }
+    trace!(?order_info);
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+// 处理订单信息
+pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
+    let price = Decimal::from_str(order["px"].as_str().unwrap()).unwrap();
+    let size = Decimal::from_str(order["sz"].as_str().unwrap()).unwrap();
+    let status = order["state"].as_str().unwrap_or("");
+    let filled_size = Decimal::from_str(order["accFillSz"].as_str().unwrap()).unwrap();
+
+    let avg_price = Decimal::from_str(order["avgPx"].as_str().unwrap()).unwrap();
+
+    let amount = size * ct_val;
+    let deal_amount = filled_size * ct_val;
+    let custom_status = if ["canceled", "filled", "mmp_canceled"].contains(&status) {
+        "REMOVE".to_string()
+    } else if ["live", "partially_filled"].contains(&status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id: order["ordId"].as_str().unwrap().to_string(),
+        custom_id: order["clOrdId"].as_str().unwrap().to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["ordType"].as_str().unwrap().to_string(),
+        trace_stack: TraceStack::default().on_special("77 okx_handle".to_string()),
+    }
+}
+
+// 处理特殊深度数据
+pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+    HandleSwapInfo::handle_special_depth(ExchangeEnum::OkxSwap, res_data)
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+// 处理特殊Ticker信息
+pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+    let res_data_str = res_data.data;
+    let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
+    format_special_ticker(res_data_json[0].clone(), res_data.label)
+}
+
+pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+    let bids = data["bids"][0].as_array().unwrap();
+    let asks = data["asks"][0].as_array().unwrap();
+    let bp = Decimal::from_str(bids[0].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_str(bids[1].as_str().unwrap()).unwrap();
+    let ap = Decimal::from_str(asks[0].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_str(asks[1].as_str().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: label,
+        depth: depth_info,
+        ticker: ticker_info,
+    }
+}
+
+// 处理position信息
+pub fn handle_position(res_data: ResponseData, ct_val: Decimal) -> Vec<Position> {
+    let res_data_str = res_data.data;
+    let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+    let position_data = res_data_json[0]["posData"].clone();
+    let result = format_position_item(position_data, ct_val);
+    return vec![result];
+}
+
+pub fn format_position_item(position: serde_json::Value, ct_val: Decimal) -> Position {
+    let symbol = position["instId"].as_str().unwrap_or("");
+    let real_leverage = dec!(-1);
+    let currency = position["ccy"].as_str().unwrap_or("");
+    let coin: Vec<&str> = symbol.split("-").collect();
+    let avg_entry_price = Decimal::from_f64(position["avgPx"].as_f64().unwrap()).unwrap();
+    let unrealised_pnl = Decimal::from_f64(position["unrealisedPnl"].as_f64().unwrap()).unwrap();
+    let pos_margin = Decimal::from_f64(position["posSide"].as_f64().unwrap()).unwrap();
+
+    let current_qty = Decimal::from_f64(position["pos"].as_f64().unwrap()).unwrap();
+    let amount = current_qty * ct_val;
+    let position_mode = match amount {
+        amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+        amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+        _ => { PositionModeEnum::Both }
+    };
+    Position {
+        symbol: format!("{}_{}", coin[0], currency),
+        margin_level: real_leverage,
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: avg_entry_price,
+        profit: unrealised_pnl,
+        position_mode,
+        margin: pos_margin,
+    }
+}

+ 576 - 0
standard/src/okx_swap.rs

@@ -0,0 +1,576 @@
+use std::collections::{BTreeMap, HashMap};
+use std::io::{Error, ErrorKind};
+use std::str::FromStr;
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::json;
+use tracing::error;
+use exchanges::okx_swap_rest::OkxSwapRest;
+use global::trace_stack::TraceStack;
+use crate::exchange::ExchangeEnum;
+use crate::{Account, Market, okx_handle, Order, OrderCommand, Platform, Position, Ticker, utils};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct OkxSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: OkxSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl OkxSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> OkxSwap {
+        let market = Market::new();
+        let mut okx_swap = OkxSwap {
+            exchange: ExchangeEnum::OkxSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: OkxSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        okx_swap.market = OkxSwap::get_market(&mut okx_swap).await.unwrap_or(okx_swap.market);
+        return okx_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for OkxSwap {
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+
+    fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::OkxSwap }
+
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+
+    fn get_self_is_colo(&self) -> bool { self.is_colo }
+
+    fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+
+    fn get_self_market(&self) -> Market { self.market.clone() }
+
+    fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+
+    fn get_request_avg_delay(&self) -> Decimal { self.request.get_avg_delay() }
+
+    fn get_request_max_delay(&self) -> i64 { self.request.get_max_delay() }
+
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let res_data = self.request.get_server_time().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = res_data_json[0]["ts"].as_str().unwrap().to_string();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.get_balance(symbol_array[1].to_string()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let account_info = res_data_json[0]["details"][0].clone();
+            let available_balance = Decimal::from_str(&account_info["availEq"].as_str().unwrap()).unwrap();
+            let frozen_balance = Decimal::from_str(&account_info["fixedBal"].as_str().unwrap()).unwrap();
+            let balance = available_balance + frozen_balance;
+            let result = Account {
+                coin: symbol_array[1].to_string(),
+                balance,
+                available_balance,
+                frozen_balance,
+                stocks: Decimal::ZERO,
+                available_stocks: Decimal::ZERO,
+                frozen_stocks: Decimal::ZERO,
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol_format = 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 res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let position_info = res_data_json.iter().find(|&item| item["instId"].as_str().unwrap() == 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.clone(), ct_val);
+                    Ok(vec![result])
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        let 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 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_position_item(item.clone(), ct_val)).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        let symbol_format = 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 res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let ticker_info = res_data_json.iter().find(|item| item["instId"].as_str().unwrap() == format!("{}-SWAP", symbol_format.clone()));
+            match ticker_info {
+                None => {
+                    error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
+                    panic!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: value["ts"].as_str().unwrap().parse().unwrap(),
+                        high: Decimal::from_str(value["high24h"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low24h"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["askPx"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["bidPx"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["lastSz"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "-");
+        let res_data = self.request.get_ticker(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 ticker_info = res_data_json.iter().find(|item| item["instId"].as_str().unwrap() == format!("{}-SWAP", symbol_format.clone()));
+            match ticker_info {
+                None => {
+                    error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
+                    panic!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: value["ts"].as_str().unwrap().parse().unwrap(),
+                        high: Decimal::from_str(value["high24h"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low24h"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["askPx"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["bidPx"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["lastSz"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let res_data = self.request.get_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));
+            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 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,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "-");
+        let res_data = self.request.get_instruments().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<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));
+            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 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,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_order(self.symbol.clone(), order_id.to_string(), custom_id.to_string()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = 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);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_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();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let ct_val = self.market.ct_val;
+        let mut params = json!({
+            "tdMode": "cross",
+            "clOrdId": custom_id.to_string(),
+            "instId": symbol_format,
+            "px": price.to_string(),
+        });
+        params["ordType"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
+        let size = (amount / ct_val).floor();
+        params["sz"] = json!(size);
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+            }
+            "pd" => {
+                params["side"] = json!("sell");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+            }
+            "pk" => {
+                params["side"] = json!("buy");
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == "200" {
+            let res_data_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);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "-");
+        let mut params = json!({
+            "tdMode": "cross",
+            "clOrdId": custom_id.to_string(),
+            "instId": symbol_format,
+            "px": price.to_string(),
+        });
+        params["ordType"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
+        let size = (amount / ct_val).floor();
+        params["sz"] = json!(size);
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+            }
+            "pd" => {
+                params["side"] = json!("sell");
+            }
+            "kk" => {
+                params["side"] = json!("sell");
+            }
+            "pk" => {
+                params["side"] = json!("buy");
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == "200" {
+            let res_data_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);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let res_data = self.request.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();
+            let order_info = res_data_json[0].clone();
+            let id = order_info["ordId"].as_str().unwrap().to_string();
+            let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
+            let result = Order {
+                id,
+                custom_id,
+                price: Decimal::ZERO,
+                amount: Decimal::ZERO,
+                deal_amount: Decimal::ZERO,
+                avg_price: Decimal::ZERO,
+                status: "REMOVE".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("428 okx_handle".to_string()),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        let res_data = self.request.set_position_mode().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
+        let res_data = self.request.set_leverage(symbol_format, leverage.to_string()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string())) }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string())) }
+
+    async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
+        let mut handles = vec![];
+        // 撤销订单
+        let cancel = order_command.cancel;
+        for item in cancel.keys() {
+            let mut self_clone = self.clone();
+            let cancel_clone = cancel.clone();
+            let item_clone = item.clone();
+            let order_id = cancel_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+            let custom_id = cancel_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+            let result_sd = self.order_sender.clone();
+            let err_sd = self.error_sender.clone();
+            let handle = tokio::spawn(async move {
+                if order_id != "" {
+                    let result = self_clone.cancel_order(&order_id, &custom_id).await;
+                    match result {
+                        Ok(_) => {
+                            // result_sd.send(result).await.unwrap();
+                        }
+                        Err(error) => {
+                            // 取消失败去查订单。
+                            let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
+                            match query_rst {
+                                Ok(order) => {
+                                    result_sd.send(order).await.unwrap();
+                                }
+                                Err(query_err) => {
+                                    error!(?query_err);
+                                    error!("撤单失败,而且查单也失败了,okx_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
+                                }
+                            }
+                            err_sd.send(error).await.unwrap();
+                        }
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        // 下单指令
+        let mut limits = HashMap::new();
+        limits.extend(order_command.limits_open);
+        limits.extend(order_command.limits_close);
+        for item in limits.keys() {
+            let mut self_clone = self.clone();
+            let limits_clone = limits.clone();
+            let item_clone = item.clone();
+            let result_sd = self.order_sender.clone();
+            let err_sd = self.error_sender.clone();
+            let mut ts = trace_stack.clone();
+
+            let handle = tokio::spawn(async move {
+                let value = limits_clone[&item_clone].clone();
+                let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
+                let side = value.get(1).unwrap();
+                let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
+                let cid = value.get(3).unwrap();
+
+                //  order_name: [数量,方向,价格,c_id]
+                let result = self_clone.take_order(cid, side, price, amount).await;
+                match result {
+                    Ok(mut result) => {
+                        ts.on_after_send();
+                        result.trace_stack = ts.clone();
+
+                        result_sd.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        let mut err_order = Order::new();
+                        err_order.custom_id = cid.clone();
+                        err_order.status = "REMOVE".to_string();
+
+                        result_sd.send(err_order).await.unwrap();
+                        err_sd.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+        // 检查订单指令
+        let check = order_command.check;
+        for item in check.keys() {
+            let mut self_clone = self.clone();
+            let check_clone = check.clone();
+            let item_clone = item.clone();
+            let order_id = check_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
+            let custom_id = check_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
+            let result_sd = self.order_sender.clone();
+            let err_sd = self.error_sender.clone();
+            let handle = tokio::spawn(async move {
+                let result = self_clone.get_order_detail(&order_id, &custom_id).await;
+                match result {
+                    Ok(result) => {
+                        result_sd.send(result).await.unwrap();
+                    }
+                    Err(error) => {
+                        err_sd.send(error).await.unwrap();
+                    }
+                }
+            });
+            handles.push(handle)
+        }
+
+        let futures = FuturesUnordered::from_iter(handles);
+        let _: Result<Vec<_>, _> = futures.try_collect().await;
+    }
+}

+ 31 - 0
standard/tests/binance_spot_handle_test.rs

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

+ 195 - 0
standard/tests/bitget_spot_test.rs

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

+ 186 - 31
standard/tests/exchange_test.rs

@@ -7,13 +7,18 @@ use std::time::Duration;
 use rust_decimal_macros::dec;
 use tokio::sync::mpsc::{channel, Receiver, Sender};
 use tokio::try_join;
-use tracing::trace;
-use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
+use tracing::{error, trace};
+// 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::response_base::ResponseData;
 use standard::exchange::{Exchange, ExchangeEnum};
-use standard::{kucoin_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};
 
 // 创建实体
+#[allow(dead_code)]
 pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn Platform> {
     utils::proxy_handle();
     let (order_sender, _order_receiver): (Sender<Order>, Receiver<Order>) = channel(1024);
@@ -61,40 +66,181 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
             params.insert("pass_key".to_string(), pass_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
+        ExchangeEnum::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());
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), pass_key);
+            Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        }
+        ExchangeEnum::BitgetSpot => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = 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);
+            Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        }
     }
 }
 
 #[allow(dead_code)]
-pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<KucoinSubscribeType>: From<T> {
+pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<OkxSubscribeType>: From<T> {
     utils::proxy_handle();
 
     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 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 mut exchange_wss;
+            // exchange_wss = BinanceSpotWs::new_label(name, false, params, BinanceSpotWsType::PublicAndPrivate, res_sender);
+            // 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() {
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if received.data != "" {
+            //                         let result = binance_handle::handle_special_depth(received);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if received.data != "" {
+            //                         let result = binance_handle::handle_special_ticker(received);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     }
+            // });
+            // try_join!(t1, t2).unwrap();
+            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
+        }
         ExchangeEnum::BinanceSwap => {
-            panic!("该交易所不支持!")
+            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
         }
         ExchangeEnum::GateSwap => {
-            panic!("该交易所不支持!")
+            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
         }
         ExchangeEnum::KucoinSwap => {
-            let symbol_format = format!("{}M", utils::format_symbol(symbol.to_string(), ""));
+            // 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 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) {
+            //     exchange_wss = KucoinSwapWs::new_label(name, false, params, KucoinWsType::Public, res_sender).await
+            // } else {
+            //     exchange_wss = KucoinSwapWs::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() {
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     let result = kucoin_handle::handle_special_depth(received);
+            //                     trace!(?result)
+            //                 }
+            //                 "ticker" => {
+            //                     let result = kucoin_handle::handle_special_ticker(received);
+            //                     trace!(?result)
+            //                 }
+            //                 "account" => {
+            //                     trace!(?received);
+            //                     let result = kucoin_handle::handle_account_info(received, symbol_back.clone());
+            //                     trace!(?result)
+            //                 }
+            //                 "position" => {
+            //                     trace!(?received);
+            //                     let result = kucoin_handle::handle_position(received, dec!(1));
+            //                     trace!(?result)
+            //                 }
+            //                 "orders" => {
+            //                     trace!(?received);
+            //                     let result = kucoin_handle::handle_order(received, dec!(0.001));
+            //                     trace!(?result)
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     }
+            // });
+            // try_join!(t1, t2).unwrap();
+        }
+        ExchangeEnum::OkxSwap => {
+            let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
             let symbol_back = utils::format_symbol(symbol.to_string(), "_");
-            let name = format!("kucoin_usdt_swap@{}", symbol.to_string().to_lowercase());
+            trace!(symbol_format);
+            let name = format!("okx_usdt_swap@{}", 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());
+            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(), pass_key);
-            let mut exchange_wss;
-            if ["depth", "ticker"].contains(&mold) {
-                exchange_wss = KucoinSwapWs::new_label(name, false, params, KucoinWsType::Public, res_sender).await
+            params.insert("pass_key".to_string(), passphrase);
+
+            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)
             } else {
-                exchange_wss = KucoinSwapWs::new_label(name, false, params, KucoinWsType::Private, res_sender).await
-            }
+                OkxSwapWs::new_label(name, false, params, OkxWsType::Business, res_sender)
+            };
+
             exchange_wss.set_subscribe(subscriber_type.into());
 
             let t1 = tokio::spawn(async move {
@@ -108,29 +254,37 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
                     if let Ok(received) = res_receiver.try_recv() {
                         match mold_clone.as_str() {
                             "depth" => {
-                                let result = kucoin_handle::handle_special_depth(received);
-                                trace!(?result)
+                                if received.data != "" {
+                                    let result = okx_handle::handle_special_depth(received);
+                                    trace!(?result)
+                                }
                             }
                             "ticker" => {
-                                let result = kucoin_handle::handle_special_ticker(received);
-                                trace!(?result)
+                                if received.data != "" {
+                                    let result = okx_handle::handle_special_ticker(received);
+                                    trace!(?result)
+                                }
                             }
                             "account" => {
-                                trace!(?received);
-                                let result = kucoin_handle::handle_account_info(received, symbol_back.clone());
-                                trace!(?result)
+                                if received.data != "" {
+                                    let result = okx_handle::handle_account_info(received, symbol_back.clone());
+                                    trace!(?result)
+                                }
                             }
                             "position" => {
-                                trace!(?received);
-                                let result = kucoin_handle::handle_position(received, dec!(1));
-                                trace!(?result)
+                                if received.data != "" {
+                                    let result = okx_handle::handle_position(received, dec!(10));
+                                    trace!(?result)
+                                }
                             }
                             "orders" => {
-                                trace!(?received);
-                                let result = kucoin_handle::handle_order(received, dec!(0.001));
-                                trace!(?result)
+                                if received.data != "" {
+                                    let result = okx_handle::handle_order(received, dec!(10));
+                                    trace!(?result)
+                                }
                             }
                             _ => {
+                                error!("没有该命令!mode={}", mold_clone);
                                 panic!("没有该命令!mode={}", mold_clone)
                             }
                         }
@@ -140,7 +294,8 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             try_join!(t1, t2).unwrap();
         }
         _ => {
-            panic!("该交易所不支持!")
+            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
         }
     }
 }

+ 1 - 1
standard/tests/gate_swap_test.rs

@@ -266,7 +266,7 @@ async fn test_command_order() {
     command.limits_open.insert("888888".to_string(), vec!["100".to_string(), "kd".to_string(), "0.18".to_string(), "888888".to_string()]);
     command.limits_close.insert("999999".to_string(), vec!["100".to_string(), "kk".to_string(), "0.25".to_string(), "999999".to_string()]);
     command.check.insert("888888".to_string(), vec!["999999".to_string(), "".to_string()]);
-    gate_swap_exchange.command_order(command).await;
+    gate_swap_exchange.command_order(command, Default::default()).await;
 
     loop {
         if let Ok(order) = order_receiver.try_recv() {

+ 13 - 1
standard/tests/kucoin_swap_test.rs

@@ -221,6 +221,18 @@ async fn test_cancel_orders() {
     trace!(?kucoin_cancel_orders);
 }
 
+
+// 测试设置自动追加保证金
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_set_auto_deposit_status() {
+    global::log_utils::init_log_with_trace();
+
+    let mut kucoin_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::KucoinSwap, SYMBOL).await;
+    let kucoin_set_auto_deposit_status = kucoin_swap_exchange.set_auto_deposit_status(true).await;
+    trace!(?kucoin_set_auto_deposit_status);
+}
+
 // 测试指令下单
 #[tokio::test]
 #[instrument(level = "TRACE")]
@@ -245,7 +257,7 @@ async fn test_command_order() {
     command.limits_open.insert("888888".to_string(), vec!["100".to_string(), "kd".to_string(), "0.18".to_string(), "888888".to_string()]);
     command.limits_close.insert("999999".to_string(), vec!["100".to_string(), "kk".to_string(), "0.25".to_string(), "999999".to_string()]);
     // command.check.insert("888888".to_string(), vec!["999999".to_string(), "94647166466789377".to_string()]);
-    kucoin_swap_exchange.command_order(command).await;
+    kucoin_swap_exchange.command_order(command, Default::default()).await;
 
 
     loop {

+ 68 - 0
standard/tests/okx_handle_test.rs

@@ -0,0 +1,68 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::okx_swap_ws::OkxSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "ARB_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PuBooks50L2tbt
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "depth").await;
+}
+
+// 测试订阅Ticker信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PuBooks5
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "ticker").await;
+}
+
+// 测试订阅Account信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_account() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PrAccount("USDT".to_string())
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "account").await;
+}
+
+// 测试订阅Position信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_position() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PrBalanceAndPosition
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "position").await;
+}
+
+// 测试订阅Orders信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PrOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "orders").await;
+}

+ 195 - 0
standard/tests/okx_swap_test.rs

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

+ 313 - 55
strategy/src/exchange_disguise.rs

@@ -1,19 +1,22 @@
 use std::collections::BTreeMap;
-use std::str::FromStr;
 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 tracing::{error, info};
+use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
 use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
+use exchanges::bitget_spot_ws::{BitgetSpotWs, BitgetSubscribeType, BitgetWsType};
 use exchanges::gate_swap_rest::GateSwapRest;
 use exchanges::gate_swap_ws::{GateSubscribeType, GateSwapWs, GateWsType};
 use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
-use standard::exchange::ExchangeEnum::{BinanceSwap, GateSwap, KucoinSwap};
+use global::trace_stack::TraceStack;
+use standard::exchange::ExchangeEnum::{BinanceSpot, BinanceSwap, BitgetSpot, GateSwap, KucoinSwap};
 use standard::SpecialTicker;
 use crate::model::{OrderInfo, OriginalTicker, OriginalTradeBa, OriginalTradeGa};
 use crate::quant::Quant;
@@ -26,9 +29,12 @@ 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_usdt_spot" => {
+            bitget_spot_run(bool_v1,1i8, quant_arc, name, symbols, exchange_params).await;
         }
         _ => {
-            panic!("参数错误!")
+            error!("交易交易所启动失败,参数错误!")
         }
     }
 }
@@ -39,14 +45,20 @@ 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;
+        }
         "gate_usdt_swap" => {
-            gate_swap_run(bool_v1, 0i8, quant_arc, name, symbols, exchange_params).await;
+            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_swap_run(bool_v1,0i8, quant_arc, name, symbols, exchange_params).await;
+        },
+        "bitget_usdt_spot" => {
+            bitget_spot_run(bool_v1,0i8, quant_arc, name, symbols, exchange_params).await;
         }
         _ => {
-            panic!("参数错误!")
+            error!("参考交易所启动失败,参数错误!")
         }
     }
 }
@@ -55,29 +67,21 @@ pub async fn run_reference_exchange(bool_v1 :Arc<AtomicBool>, exchange_name: Str
 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);
     let mut gate_exc = GateSwapRest::new(false, exchange_params.clone());
-    // 获取user_id
-    let res_data = gate_exc.wallet_fee().await;
-    assert_eq!(res_data.code, "200", "获取gate交易所参数 user_id 失败, 启动失败!");
-
-    let wallet_obj :Value = serde_json::from_str(&res_data.data).unwrap();
-    info!(?wallet_obj);
-    let user_id = wallet_obj["user_id"].to_string();
+    let mut user_id= "".to_string();
     let symbols_one = symbols.clone();
-    // 获取乘数(计价货币兑换为结算货币的乘数)
-    let response = gate_exc.get_market_details("usdt".to_string()).await;
-    assert_eq!(response.code, "200", "获取gate交易所参数 multiplier 失败, 启动失败!");
 
-    let contract_obj :Vec<Value> = parse_json_array(&response.data).unwrap();
-    let mut multiplier =  Decimal::ZERO;
-    for val in contract_obj {
-        if symbols[0].to_uppercase() == val["name"].as_str().unwrap(){
-            let num = val["quanto_multiplier"].as_str().unwrap();
-            multiplier = Decimal::from_str(num).unwrap();
-        }
+    // 交易
+    if type_num == 1{
+        // 获取user_id
+        let res_data = gate_exc.wallet_fee().await;
+        assert_eq!(res_data.code, "200", "获取gate交易所参数 user_id 失败, 启动失败!");
+
+        let wallet_obj :Value = serde_json::from_str(&res_data.data).unwrap();
+        info!(?wallet_obj);
+        user_id = wallet_obj["user_id"].to_string();
     }
-    assert_ne!(multiplier, Decimal::ZERO, "获取gate交易所参数 multiplier 为0!");
 
-    tokio::spawn( async move {
+    spawn( async move {
         let mut gate_exc = GateSwapWs::new_label(name, false, exchange_params,
                                                  GateWsType::PublicAndPrivate("usdt".to_string()), tx);
         // 交易
@@ -97,25 +101,34 @@ async fn gate_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mu
         }
         gate_exc.custom_subscribe(bool_v1,symbols_one).await;
     });
-    tokio::spawn(async move {
+    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;
         let run_symbol = symbols.clone()[0].clone();
         // trade
         let mut max_buy = Decimal::ZERO;
         let mut min_sell = Decimal::ZERO;
         loop {
             sleep(Duration::from_millis(1)).await;
+            let mut trace_stack = TraceStack::default();
+
             match rx.recv().await {
                 Some(data) => {
+                    trace_stack.on_network(data.time);
+                    trace_stack.on_before_quant();
+
                     if data.code != "200".to_string() {
                         continue;
                     }
                     if data.channel == "futures.order_book" {
-                        let depth = standard::gate_handle::handle_special_depth(data);
+                        trace_stack.on_before_format();
+                        let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(GateSwap, 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());
+                            quant._update_depth(depth.depth.clone(), depth.name.clone(), &mut trace_stack);
                             quant.local_depths.insert(depth.name, depth.depth);
                         }
                     } else if data.channel == "futures.balances" {
@@ -125,7 +138,10 @@ async fn gate_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mu
                             quant.update_equity(account);
                         }
                     } else if data.channel == "futures.orders" {
+                        trace_stack.on_before_format();
                         let orders = standard::handle_info::HandleSwapInfo::handle_order(GateSwap, data.clone(), multiplier.clone());
+                        trace_stack.on_after_format();
+
                         let mut order_infos:Vec<OrderInfo> = Vec::new();
                         for order in orders.order {
                             let order_info = OrderInfo {
@@ -141,12 +157,14 @@ async fn gate_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<Mu
                                 create_time: 0,
                                 status: order.status,
                                 fee: Default::default(),
+                                trace_stack: Default::default(),
                             };
                             order_infos.push(order_info);
                         }
+
                         {
                             let mut quant = bot_arc_clone.lock().await;
-                            quant.update_order(order_infos);
+                            quant.update_order(order_infos, trace_stack);
                         }
                     } else if data.channel == "futures.positions" {
                         let positions = standard::handle_info::HandleSwapInfo::handle_position(GateSwap,data, multiplier.clone());
@@ -189,10 +207,10 @@ async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<
     let symbols_clone = symbols.clone();
     let mut symbol_arr = Vec::new();
     for symbol in symbols_clone{
-       let new_symbol = symbol.replace("_", "").to_uppercase() + "M";
+        let new_symbol = symbol.replace("_", "").to_uppercase() + "M";
         symbol_arr.push(new_symbol);
     }
-    tokio::spawn( async move {
+    spawn( async move {
         let mut kucoin_exc;
         // 交易
         if type_num == 1 {
@@ -215,26 +233,34 @@ async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<
         }
         kucoin_exc.custom_subscribe(bool_v1, symbol_arr).await;
     });
-    tokio::spawn(async move {
+    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().amount_size;
+        let multiplier = bot_arc_clone.lock().await.platform_rest.get_self_market().ct_val;
         loop {
             sleep(Duration::from_millis(1)).await;
+            let mut trace_stack = TraceStack::default();
+
             match rx.recv().await {
                 Some(data) => {
+                    trace_stack.on_network(data.time);
+                    trace_stack.on_before_quant();
+
                     if data.code != "200".to_string() {
                         continue;
                     }
                     if data.channel == "level2" {
+                        trace_stack.on_before_format();
                         let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(KucoinSwap,data);
+                        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());
+                            quant._update_depth(depth.depth.clone(), depth.name.clone(), &mut trace_stack);
                             quant.local_depths.insert(depth.name, depth.depth);
                         }
                     } else if data.channel == "tickerV2" {
@@ -251,7 +277,9 @@ async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<
                             quant.update_equity(account);
                         }
                     } else if data.channel == "symbolOrderChange" {
+                        trace_stack.on_before_format();
                         let orders = standard::handle_info::HandleSwapInfo::handle_order(KucoinSwap, data.clone(), multiplier);
+                        trace_stack.on_after_format();
                         let mut order_infos:Vec<OrderInfo> = Vec::new();
                         for order in orders.order {
                             if order.status == "NULL" {
@@ -270,12 +298,14 @@ async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<
                                 create_time: 0,
                                 status: order.status,
                                 fee: Default::default(),
+                                trace_stack: Default::default(),
                             };
                             order_infos.push(order_info);
                         }
+
                         {
                             let mut quant = bot_arc_clone.lock().await;
-                            quant.update_order(order_infos);
+                            quant.update_order(order_infos, trace_stack);
                         }
                     } else if data.channel == "position.change" {
                         let positions = standard::handle_info::HandleSwapInfo::handle_position(KucoinSwap,data, multiplier);
@@ -286,19 +316,17 @@ async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<
                     } else if data.channel == "match" {
                         let mut quant = bot_arc_clone.lock().await;
                         let str = data.label.clone();
-                        if quant.is_update.contains_key(&data.label) && *quant.is_update.get(str.as_str()).unwrap(){
+                        if quant.is_update.contains_key(&data.label) && *quant.is_update.get(str.as_str()).unwrap() {
                             max_buy = Decimal::ZERO;
                             min_sell = Decimal::ZERO;
                             quant.is_update.remove(str.as_str());
                         }
-                        let trades: Vec<OriginalTradeGa> = serde_json::from_str(data.data.as_str()).unwrap();
-                        for trade in trades {
-                            if trade.price > max_buy || max_buy == Decimal::ZERO{
-                                max_buy = trade.price
-                            }
-                            if trade.price < min_sell || min_sell == Decimal::ZERO{
-                                min_sell = trade.price
-                            }
+                        let trade: OriginalTradeGa = serde_json::from_str(data.data.as_str()).unwrap();
+                        if trade.price > max_buy || max_buy == Decimal::ZERO {
+                            max_buy = trade.price
+                        }
+                        if trade.price < min_sell || min_sell == Decimal::ZERO {
+                            min_sell = trade.price
                         }
                         quant.max_buy_min_sell_cache.insert(data.label, vec![max_buy, min_sell]);
                     }
@@ -312,10 +340,110 @@ async fn kucoin_swap_run(bool_v1 :Arc<AtomicBool>, type_num: i8, quant_arc: Arc<
     });
 }
 
+// 参考 币安 现货 启动
+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;
+            let mut trace_stack = TraceStack::default();
+
+            match rx.recv().await {
+                Some(data) => {
+                    trace_stack.on_network(data.time);
+                    trace_stack.on_before_quant();
+
+                    if data.code != "200".to_string() {
+                        continue;
+                    }
+                    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;
+                        if quant.is_update.contains_key(&data.label) && *quant.is_update.get(str.as_str()).unwrap() {
+                            max_buy = Decimal::ZERO;
+                            min_sell = Decimal::ZERO;
+                            quant.is_update.remove(str.as_str());
+                        }
+                        if trade.p > max_buy || max_buy == Decimal::ZERO{
+                            max_buy = trade.p
+                        }
+                        if trade.p < min_sell || min_sell == Decimal::ZERO{
+                            min_sell = trade.p
+                        }
+                        {
+                            quant.max_buy_min_sell_cache.insert(data.label, vec![max_buy, min_sell]);
+                        }
+                    } 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;
+                                // 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(), &mut trace_stack);
+                                quant.local_depths.insert(data.label.clone(), depth);
+                            }
+                        } else {
+                            update_flag_u = ticker.u;
+                        }
+                    } 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["lastUpdateId"].as_i64().unwrap();
+                        if u > update_flag_u {
+                            let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(BinanceSpot, 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(), &mut trace_stack);
+                                quant.local_depths.insert(depth.name, depth.depth);
+                            }
+                        } else {
+                            update_flag_u = u;
+                        }
+
+                    }
+                },
+                None => {
+                    error!("币安现货参考交易所通道错误");
+                    break;
+                }
+            }
+
+        }
+    });
+}
+
+
 // 参考 币安 合约 启动
 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);
-    tokio::spawn( async move {
+    spawn( async move {
         let mut ba_exc = BinanceSwapWs::new_label(name, false, exchange_params, BinanceWsType::PublicAndPrivate, tx);
         ba_exc.set_subscribe(vec![
             BinanceSubscribeType::PuBookTicker,
@@ -323,7 +451,7 @@ async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mut
         ]);
         ba_exc.custom_subscribe(bool_v1, symbols.clone()).await;
     });
-    tokio::spawn(async move {
+    spawn(async move {
         // trade
         let mut max_buy = Decimal::ZERO;
         let mut min_sell = Decimal::ZERO;
@@ -332,8 +460,13 @@ async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mut
         let bot_arc_clone = Arc::clone(&quant_arc);
         loop {
             sleep(Duration::from_millis(1)).await;
+            let mut trace_stack = TraceStack::default();
+
             match rx.recv().await {
                 Some(data) => {
+                    trace_stack.on_network(data.time);
+                    trace_stack.on_before_quant();
+
                     if data.code != "200".to_string() {
                         continue;
                     }
@@ -356,6 +489,7 @@ async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mut
                             quant.max_buy_min_sell_cache.insert(data.label, vec![max_buy, min_sell]);
                         }
                     } 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 {
                             {
@@ -366,21 +500,150 @@ async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mut
                                     mid_price: Default::default(),
                                 }, data.label.clone());
                                 let depth = vec![ticker.b, ticker.B, ticker.a, ticker.A];
-                                quant._update_depth(depth.clone(), data.label.clone());
+                                trace_stack.on_after_format();
+                                quant._update_depth(depth.clone(), data.label.clone(), &mut trace_stack);
                                 quant.local_depths.insert(data.label.clone(), depth);
                             }
                         } else {
                             update_flag_u = ticker.u;
                         }
                     } else if data.channel == "depth" {
-                        // TODO: self._check_update_u(msg['u']):
-                        let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(BinanceSwap, data);
+                        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(), &mut trace_stack);
+                                quant.local_depths.insert(depth.name, depth.depth);
+                            }
+                        } else {
+                            update_flag_u = u;
+                        }
+                    }
+                },
+                None => {
+                    error!("参考交易所通道错误");
+                    break;
+                }
+            }
+        }
+    });
+}
+
+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();
+    spawn( async move {
+        let mut bit_exc_public = BitgetSpotWs::new_label(name.clone(), false, exchange_params.clone(), BitgetWsType::Public, tx.clone());
+        let mut bit_exc_private = BitgetSpotWs::new_label(name, false, exchange_params, BitgetWsType::Private, tx);
+        // 交易
+        if type_num == 1 {
+            bit_exc_private.set_subscribe(vec![
+                BitgetSubscribeType::PrAccount,
+                BitgetSubscribeType::PrOrders
+            ]);
+            bit_exc_public.set_subscribe(vec![
+                BitgetSubscribeType::PuTrade,
+                BitgetSubscribeType::PuBooks5
+            ]);
+        } else { // 参考
+            bit_exc_public.set_subscribe(vec![
+                BitgetSubscribeType::PuTrade,
+                BitgetSubscribeType::PuBooks5
+            ]);
+        }
+        bit_exc_public.custom_subscribe(bool_v1.clone(), symbols_1.clone()).await;
+        bit_exc_private.custom_subscribe(bool_v1, symbols_1).await;
+    });
+    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;
+        // trade
+        let mut max_buy = Decimal::ZERO;
+        let mut min_sell = Decimal::ZERO;
+        let run_symbol = symbols.clone()[0].clone();
+
+        loop {
+            sleep(Duration::from_millis(1)).await;
+            let mut trace_stack = TraceStack::default();
+
+            match rx.recv().await {
+                Some(data) => {
+                    trace_stack.on_network(data.time);
+                    trace_stack.on_before_quant();
+
+                    if data.code != "200".to_string() {
+                        continue;
+                    }
+                    if data.channel == "books5" {
+                        trace_stack.on_before_format();
+                        let depth = standard::handle_info::HandleSwapInfo::handle_special_depth(BitgetSpot, 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());
+                            quant._update_depth(depth.depth.clone(), depth.name.clone(), &mut trace_stack);
                             quant.local_depths.insert(depth.name, depth.depth);
                         }
+                    } else if data.channel == "trade" {
+                        let mut quant = bot_arc_clone.lock().await;
+                        let str = data.label.clone();
+                        if quant.is_update.contains_key(&data.label) && *quant.is_update.get(str.as_str()).unwrap(){
+                            max_buy = Decimal::ZERO;
+                            min_sell = Decimal::ZERO;
+                            quant.is_update.remove(str.as_str());
+                        }
+                        let trades: Vec<OriginalTradeGa> = serde_json::from_str(data.data.as_str()).unwrap();
+                        for trade in trades {
+                            if trade.price > max_buy || max_buy == Decimal::ZERO{
+                                max_buy = trade.price
+                            }
+                            if trade.price < min_sell || min_sell == Decimal::ZERO{
+                                min_sell = trade.price
+                            }
+                        }
+                        quant.max_buy_min_sell_cache.insert(data.label, vec![max_buy, min_sell]);
+                    } else if data.channel == "orders" {
+                        trace_stack.on_before_format();
+                        let orders = standard::handle_info::HandleSwapInfo::handle_order(BitgetSpot, data.clone(), multiplier);
+                        trace_stack.on_after_format();
+                        let mut order_infos:Vec<OrderInfo> = Vec::new();
+                        for order in orders.order {
+                            if order.status == "NULL" {
+                                continue;
+                            }
+                            let order_info = OrderInfo {
+                                symbol: "".to_string(),
+                                amount: order.amount.abs(),
+                                side: "".to_string(),
+                                price: order.price,
+                                client_id: order.custom_id,
+                                filled_price: order.avg_price,
+                                filled: order.deal_amount.abs(),
+                                order_id: order.id,
+                                local_time: 0,
+                                create_time: 0,
+                                status: order.status,
+                                fee: Default::default(),
+                                trace_stack: Default::default(),
+                            };
+                            order_infos.push(order_info);
+                        }
+                        {
+                            let mut quant = bot_arc_clone.lock().await;
+                            quant.update_order(order_infos, trace_stack);
+                        }
+                    } else if data.channel == "account" { // _update_account
+                        // TODO: 此处应有两个余额,交易币和本位币余额(由于quant中现货的更新账户没有做任何操作,所以此处可暂不处理)
+                        let account = standard::handle_info::HandleSwapInfo::handle_account_info(BitgetSpot, data, run_symbol.clone());
+                        {
+                            let mut quant = bot_arc_clone.lock().await;
+                            quant.update_equity(account);
+                        }
                     }
                 },
                 None => {
@@ -390,9 +653,4 @@ async fn reference_binance_swap_run(bool_v1 :Arc<AtomicBool>, quant_arc: Arc<Mut
             }
         }
     });
-}
-
-
-fn parse_json_array(json: &str) -> serde_json::Result<Vec<Value>> {
-    serde_json::from_str(json)
 }

+ 0 - 0
strategy/src/gp_predictor.rs


+ 2 - 1
strategy/src/lib.rs

@@ -3,4 +3,5 @@ pub mod model;
 mod strategy;
 mod predictor;
 mod utils;
-pub mod exchange_disguise;
+pub mod exchange_disguise;
+mod gp_predictor;

+ 14 - 7
strategy/src/model.rs

@@ -2,9 +2,17 @@ use std::collections::{HashMap};
 use rust_decimal::Decimal;
 use rust_decimal_macros::dec;
 use serde_derive::{Deserialize, Serialize};
+use global::trace_stack::TraceStack;
+
+pub struct TokenParam {
+    // 平台币名称
+    pub token: String,
+    // 最低持有价值(u)
+    pub limit_value: Decimal
+}
 
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub struct LocalPosition{
+pub struct LocalPosition {
     // 做多仓位
     pub long_pos: Decimal,
     // 做空仓位
@@ -27,7 +35,7 @@ impl LocalPosition {
 }
 
 #[derive(Debug, Clone)]
-pub struct TraderMsg{
+pub struct TraderMsg {
     pub position: LocalPosition,
     pub cash: Decimal,
     pub coin: Decimal,
@@ -57,8 +65,7 @@ impl TraderMsg {
 }
 
 #[derive(Debug, Clone)]
-pub struct OrderInfo{
-
+pub struct OrderInfo {
     pub symbol: String,
 
     pub amount: Decimal,
@@ -81,7 +88,9 @@ pub struct OrderInfo{
 
     pub status: String,
 
-    pub fee: Decimal
+    pub fee: Decimal,
+    // 追踪体系
+    pub trace_stack: TraceStack
 }
 
 #[derive(Serialize, Deserialize)]
@@ -94,9 +103,7 @@ pub struct OriginalTradeBa {
 
 #[derive(Serialize, Deserialize)]
 pub struct OriginalTradeGa {
-    // 成交价格
     pub size: Decimal,
-    // 买方是否是做市方。如true,则此次成交是一个主动卖出单,否则是一个主动买入单。
     pub price: Decimal
 }
 

+ 336 - 173
strategy/src/quant.rs

@@ -10,26 +10,26 @@ use rust_decimal::Decimal;
 use rust_decimal::prelude::{ToPrimitive};
 use rust_decimal_macros::dec;
 use tokio::spawn;
-use tokio::sync::mpsc::{Receiver, Sender};
-use tokio::sync::Mutex;
+use tokio::sync::mpsc::{Sender};
+use tokio::sync::{Mutex};
 use tokio::task::JoinHandle;
 use tokio::time::sleep;
 use tracing::{debug, error, info, warn};
 use global::params::Params;
 use global::public_params::{ASK_PRICE_INDEX, BID_PRICE_INDEX, LENGTH};
+use global::trace_stack::TraceStack;
 use standard::{Account, Market, Order, OrderCommand, Platform, Position, PositionModeEnum, SpecialTicker, Ticker};
 use standard::exchange::{Exchange};
-use standard::exchange::ExchangeEnum::{BinanceSpot, BinanceSwap, GateSpot, GateSwap, KucoinSwap};
-
-use crate::model::{LocalPosition, OrderInfo, TraderMsg};
+use standard::exchange::ExchangeEnum::{BinanceSpot, BinanceSwap, BitgetSpot, GateSpot, GateSwap, KucoinSwap};
 
+use crate::model::{LocalPosition, OrderInfo, TokenParam, TraderMsg};
 use crate::predictor::Predictor;
 use crate::strategy::Strategy;
-use crate::utils::{clip};
+use crate::utils::clip;
 
 
 pub struct Quant {
-    pub  params: Params,
+    pub params: Params,
     // 启动时间
     pub start_time: i64,
     // 币对
@@ -38,8 +38,6 @@ pub struct Quant {
     pub base: String,
     // 报价货币
     pub quote: String,
-    // 现货底仓
-    pub hold_coin: Decimal,
     //
     pub strategy: Strategy,
     // 本地挂单表
@@ -66,7 +64,7 @@ pub struct Quant {
     pub local_buy_value: Decimal,
     pub local_sell_value: Decimal,
     pub local_cancel_log: HashMap<String, i64>,
-    pub interval: Decimal,
+    pub interval: u64,
     pub exchange: String,
     pub trade_msg: TraderMsg,
     pub exit_msg: String,
@@ -102,6 +100,7 @@ pub struct Quant {
     pub local_depths: HashMap<String, Vec<Decimal>>,
     pub is_update: HashMap<String, bool>,
     pub running: Arc<AtomicBool>,
+    pub hold_coin: Decimal,
 }
 
 impl Quant {
@@ -114,7 +113,8 @@ impl Quant {
             symbol: symbol.clone(),
             base: pairs[0].to_string(),
             quote: pairs[1].to_string(),
-            hold_coin: clip(params.hold_coin, dec!(0.0), dec!(10000.0)),
+            // 现货底仓
+            hold_coin: clip(params.hold_coin, Decimal::ZERO, Decimal::ONE_HUNDRED*Decimal::ONE_HUNDRED),
             strategy: Strategy::new(&params, true),
             local_orders: Default::default(),
             local_orders_backup: Default::default(),
@@ -174,7 +174,6 @@ impl Quant {
                 base_asset: "".to_string(),
                 quote_asset: "".to_string(),
                 tick_size: Default::default(),
-                amount_size: Default::default(),
                 price_precision: Default::default(),
                 amount_precision: Default::default(),
                 min_qty: Default::default(),
@@ -182,25 +181,30 @@ impl Quant {
                 min_notional: Default::default(),
                 max_notional: Default::default(),
                 ct_val: Default::default(),
+                amount_size: Default::default(),
             },
             platform_rest: match exchange.as_str() {
                 "kucoin_usdt_swap" => {
-                   Exchange::new(KucoinSwap, symbol, false, exchange_params, order_sender, error_sender).await
+                   Exchange::new(KucoinSwap, symbol, params.colo != 0i8, exchange_params, order_sender, error_sender).await
                 },
                 "gate_usdt_swap" => {
-                   Exchange::new(GateSwap, symbol, false, exchange_params, order_sender, error_sender).await
+                   Exchange::new(GateSwap, symbol, params.colo != 0i8, exchange_params, order_sender, error_sender).await
                 },
                 "gate_usdt_spot" => {
-                    Exchange::new(GateSpot, symbol, false, exchange_params, order_sender, error_sender).await
+                    Exchange::new(GateSpot, symbol, params.colo != 0i8, exchange_params, order_sender, error_sender).await
                 },
                 "binance_usdt_swap" => {
-                    Exchange::new(BinanceSwap, symbol, false, exchange_params, order_sender, error_sender).await
+                    Exchange::new(BinanceSwap, symbol, params.colo != 0i8, exchange_params, order_sender, error_sender).await
+                },
+                "binance_spot" => {
+                    Exchange::new(BinanceSpot, symbol, params.colo != 0i8, exchange_params, order_sender, error_sender).await
                 },
-                "binance_usdt_spot" => {
-                    Exchange::new(BinanceSpot, symbol, false, exchange_params, order_sender, error_sender).await
+                "bitget_spot" => {
+                    Exchange::new(BitgetSpot, symbol, params.colo != 0i8, exchange_params, order_sender, error_sender).await
                 }
                 _ => {
-                    panic!("201未找到对应的交易所rest枚举!")
+                    error!("203未找到对应的交易所rest枚举!");
+                    panic!("203未找到对应的交易所rest枚举!");
                 }
             },
             max_buy_min_sell_cache: Default::default(),
@@ -263,55 +267,15 @@ impl Quant {
         return quant_obj;
     }
 
-    pub async fn handle_signals(quant_arc: Arc<Mutex<Quant>>, mut rx: Receiver<Order>) {
-        spawn(async move{
-            loop {
-                sleep(Duration::from_millis(1)).await;
-                match rx.try_recv() {
-                    Ok(val)=>{
-                        // 只处理这两种订单回执
-                        if ["NEW", "REMOVE"].contains(&val.status.as_str()){
-                            let mut local_order_info = OrderInfo{
-                                symbol: "".to_string(),
-                                amount: Default::default(),
-                                side: "".to_string(),
-                                price: Default::default(),
-                                client_id: "".to_string(),
-                                filled_price: Default::default(),
-                                filled: Default::default(),
-                                order_id: "".to_string(),
-                                local_time: 0,
-                                create_time: 0,
-                                status: "".to_string(),
-                                fee: Decimal::ZERO,
-                            };
-                            if val.status== "NEW" {
-                                local_order_info.client_id = val.custom_id;
-                                local_order_info.order_id = val.id;
-                            } else if val.status == "REMOVE" {
-                                local_order_info.client_id = val.custom_id;
-                            }
-                            let mut bot = quant_arc.lock().await;
-                            // 写入本地订单缓存
-                            bot.update_local_order(local_order_info);
-                        }
-                    },
-                    Err(e) => {
-                        info!("订单回执消费失败!{}", e);
-                        return;
-                    }
-                }
-            }
-        });
-    }
-    pub fn update_order(&mut self, data: Vec<OrderInfo>){
+    pub fn update_order(&mut self, data: Vec<OrderInfo>, trace_stack: TraceStack){
         for order in data {
-            self.update_local_order(order);
+            self.update_local_order(order, trace_stack.clone());
         }
     }
 
-    pub fn update_local_order(&mut self, data: OrderInfo) {
+    pub fn update_local_order(&mut self, data: OrderInfo, mut trace_stack: TraceStack) {
         if data.filled != Decimal::ZERO {
+            info!("\n\n");
             info!("接收到订单信息①:{:?}", data);
         }
         /*
@@ -331,6 +295,16 @@ impl Quant {
         */
         // 触发订单更新
         self.trade_order_update_time = Utc::now().timestamp_millis();
+
+        // 更新跟踪
+        if self.local_orders.contains_key(&data.client_id) {
+            let mut order_info = self.local_orders.get(&data.client_id).unwrap().clone();
+
+            if data.trace_stack.after_network != 0 { order_info.trace_stack = data.trace_stack.clone() }
+
+            self.local_orders.insert(data.client_id.clone(), order_info);
+        }
+
         // 新增订单推送 仅需要cid oid信息
         if data.status == "NEW" {
             // 更新oid信息 更新订单 loceltime信息(尤其是查单返回new的情况 必须更新 否则会误触发风控)
@@ -346,6 +320,12 @@ impl Quant {
                 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 {
@@ -483,9 +463,13 @@ impl Quant {
                                 self.local_profit -= data.fee;
                             }
                         }
+                        // info!("成交单耗时数据:{}", time_record.to_string());
                         info!("更新推算仓位 {:?}", self.local_position_by_orders);
                         // 本地计算利润
                         self._print_local_trades_summary();
+                        // 打印各类信息
+                        self.strategy.local_orders = self.local_orders.clone();
+                        self.strategy._print_summary();
                     }
                     // 每次有订单变动就触发一次策略
                     if self.mode_signal == 0 && self.ready == 1 {
@@ -494,17 +478,22 @@ impl Quant {
                         // 触发策略挂单逻辑
                         // 更新策略时间
                         self.strategy.local_time = Utc::now().timestamp_millis();
+                        trace_stack.on_before_strategy();
                         let order = self.strategy.on_time(&self.trade_msg);
+                        trace_stack.on_after_strategy();
                         // 记录指令触发信息
                         if order.is_not_empty() {
                             // info!("触发onOrder");
                             self._update_local_orders(&order);
+                            trace_stack.on_order();
                             //交易所处理订单信号
                             let mut platform_rest_fb = self.platform_rest.clone_box();
                             // info!("订单指令:{:?}", order);
+                            let mut ts = trace_stack.clone();
+                            ts.on_before_send_thread();
                             spawn(async move{
-                                // info!("update_local_order订单指令:{:?}", order);
-                                platform_rest_fb.command_order(order).await;
+                                ts.on_before_send();
+                                platform_rest_fb.command_order(order, ts.clone()).await;
                             });
                         }
                     }
@@ -569,7 +558,9 @@ impl Quant {
         self.ready = 1
     }
 
-    pub fn _update_depth(&mut self, depth: Vec<Decimal>, name :String) {
+    pub fn _update_depth(&mut self, depth: Vec<Decimal>, name: String, trace_stack: &mut TraceStack) {
+        trace_stack.on_depth();
+
         // 要从回调传入的深度信息中获取data.name
         let market_update_interval_key = name.clone();
         let market_update_time_key = name.clone();
@@ -623,18 +614,24 @@ impl Quant {
                 // 触发事件撤单逻辑
                 // 更新策略时间
                 self.strategy.local_time = Utc::now().timestamp_millis();
+
                 // 产生交易信号
+                trace_stack.on_before_strategy();
                 let orders = self.strategy.on_time(&self.trade_msg);
+                trace_stack.on_after_strategy();
                 if orders.is_not_empty() {
                     debug!("触发onTick");
                     self._update_local_orders(&orders);
-
                     //异步交易所处理订单信号
                     let mut platform_rest_fb = self.platform_rest.clone_box();
                     // info!("订单指令:{:?}", orders);
+                    let mut ts = trace_stack.clone();
+                    ts.on_order_command(orders.to_string());
+                    ts.on_before_send_thread();
                     spawn(async move{
                         // info!("_update_depth订单指令:{:?}", orders);
-                        platform_rest_fb.command_order(orders).await;
+                        ts.on_before_send();
+                        platform_rest_fb.command_order(orders, ts.clone()).await;
                     });
                 }
             }
@@ -656,10 +653,10 @@ impl Quant {
             }
         }
         // 更新仓位信息
-        info!("收到新的仓位推送, position: {:?}, local_position: {:?}", data, position);
         if position != self.local_position {
-            self.local_position = position;
+            info!("收到新的仓位推送, position: {:?}, local_position: {:?}", data, position);
             info!("更新本地仓位:{:?}", self.local_position);
+            self.local_position = position;
         }
     }
 
@@ -730,6 +727,7 @@ impl Quant {
                     create_time: self.strategy.local_time,
                     status: "".to_string(),
                     fee: Default::default(),
+                    trace_stack: Default::default(),
                 };
                 // 本地挂单表
                 self.local_orders.insert(limits.get(j).unwrap()[3].clone(), order_info.clone());
@@ -806,14 +804,14 @@ impl Quant {
         self.local_cash = data.balance * self.used_pct
     }
 
-    pub async fn update_equity_rest(&mut self) {
+    pub async fn update_equity_rest_swap(&mut self) {
         match self.platform_rest.get_account().await {
             Ok(val) => {
                 /*
-               更新保证金信息
-               合约一直更新
-               现货只有当出现异常时更新
-           */
+                   更新保证金信息
+                   合约一直更新
+                   现货只有当出现异常时更新
+               */
                 if self.exchange.contains("spot") {
                     return;
                 }
@@ -825,6 +823,34 @@ impl Quant {
         }
     }
 
+    pub async fn update_equity_rest_spot(&mut self) {
+        match self.platform_rest.get_spot_account().await {
+            Ok(mut val) => {
+                // 如果返回的数组里没有交易货币,则补充交易货币
+                if !val.iter().any(|a| a.coin.to_uppercase().eq(&self.base.to_uppercase())) {
+                    let mut base_coin_account = Account::new();
+                    base_coin_account.coin = self.base.to_uppercase();
+                    val.push(base_coin_account);
+                }
+
+                for account in val {
+                    // 交易货币
+                    if self.base.to_uppercase() == account.coin {
+                        self.local_coin = account.balance;
+                    }
+                    // 本位货币
+                    if self.quote.to_uppercase() == account.coin {
+                        self.local_cash = account.balance;
+                    }
+                }
+
+            },
+            Err(err) => {
+                error!("获取仓位信息异常: {}", err);
+            }
+        }
+    }
+
     pub async fn check_risk(&mut self) {
         // 参数检查的风控
         if self.strategy.start_cash == Decimal::ZERO {
@@ -976,63 +1002,218 @@ impl Quant {
         }
     }
 
+    pub async fn buy_token(&mut self){
+        // 买入平台币
+        // 获取U数量,平台币数量
+        // 更新账户
+        let mut cash = Decimal::ZERO;
+        let mut token = Decimal::ZERO;
+        let token_param = get_exchange_token(&self.exchange);
+        if token_param.token=="***"{
+            error!("购买平台币失败,未找到交易所的平台币!");
+            return;
+        }
+        match self.platform_rest.get_spot_account().await {
+            Ok(val) =>{
+                for account in val{
+                    if account.coin == "USDT".to_string() {
+                        cash += account.balance;
+                    }
+                    if token_param.token == account.coin {
+                        token += account.balance;
+                    }
+                }
+            },Err(err)=>{
+                error!("购买{}-获取账户失败 {}", token_param.token, err);
+            }
+        }
+        info!("持u {} , 持有平台币 {}", cash, token);
+        match self.platform_rest.get_ticker_symbol(format!("{}_USDT", token_param.token)).await {
+            Ok(val)=>{
+                let mp = (val.buy + val.sell)/Decimal::TWO;
+                let token_value = token * mp;
+                if token_value < token_param.limit_value {
+                    info!("{} 数量过少!", token_param.token);
+                    if cash > Decimal::TWO*Decimal::ONE_HUNDRED{
+                        info!("准备买入{}", token_param.token);
+                        match self.platform_rest.take_order_symbol(token_param.token, Decimal::ONE, "t-888", "kd", mp * Decimal::from_str("1.001").unwrap(), Decimal::from_str("50").unwrap()/mp).await {
+                            Ok(value) => {
+                                info!("买入平台币下单成功: {:?}", value);
+                            },
+                            Err(error) => {
+                                error!("买入平台币下单失败: {}", error)
+                            }
+                        }
+
+                    } else {
+                        info!("现金不足 无法买入{}!", token_param.token);
+                    }
+                } else {
+                    info!("{}数量充足!", token_param.token);
+                }
+            },
+            Err(err)=>{
+                error!("购买平台币-获取平台币行情失败 {}", err);
+            }
+        }
+
+
+    }
+
     pub async fn check_position(&mut self){
         info!("清空挂单!");
-        match self.platform_rest.cancel_orders().await{
+        match self.platform_rest.cancel_orders_all().await {
             Ok(val)=>{
-                info!(?val);
+                info!("清空所有挂单,{:?}", val);
             },
-            Err(e)=>{
-                error!("清空挂单异常: {}", e);
+            Err(err)=>{
+                error!("取消所有订单异常: {}",err);
+                match self.platform_rest.cancel_orders().await {
+                    Ok(val) => {
+                        info!("清空当前币对挂单,{:?}", val);
+                    },
+                    Err(exc) => {
+                        error!("清空当前币对订单异常: {}",exc);
+                    }
+                }
             }
-        };
+        }
+        if self.exchange.contains("spot") { // 现货
+            self.check_position_spot().await;
+        } else { // 合约
+            self.check_position_swap().await;
+        }
+        info!("遗留仓位检测完毕");
+    }
+
+    pub async fn check_position_spot(&mut self){
         info!("检查遗漏仓位!");
-        match self.platform_rest.get_positions().await {
-            Ok(val)=>{
-                for position in val {
-                    if !position.symbol.eq_ignore_ascii_case(self.symbol.as_str()){
+        match self.platform_rest.get_spot_account().await {
+            Ok(mut val) => {
+                // 如果返回的数组里没有交易货币,则补充交易货币
+                if !val.iter().any(|a| a.coin.to_uppercase().eq(&self.base.to_uppercase())) {
+                    let mut base_coin_account = Account::new();
+                    base_coin_account.coin = self.base.to_uppercase();
+                    val.push(base_coin_account);
+                }
+
+                // 仓位补货、卖货
+                for account in val {
+                    let coin_name = account.coin.to_uppercase();
+                    if check_coin(&self.exchange, &coin_name){
+                        continue;
+                    }
+                    let symbol = format!("{}_USDT", coin_name);
+                    let mut _hold_coin = Decimal::ZERO;
+                    if coin_name.eq(self.base.as_str()){
+                        _hold_coin = self.hold_coin;
+                    }
+                    let ap;
+                    let bp;
+                    let mp;
+                    match self.platform_rest.get_ticker().await {
+                        Ok(ticker)=>{
+                            ap = ticker.sell;
+                            bp = ticker.buy;
+                            mp = (ap + bp)/Decimal::TWO;
+                        },
+                        Err(_e)=>{
+                            error!("清仓中- 获取 {} 币对行情错误,该币对无法调整仓位。", symbol);
+                            continue;
+                        }
+                    }
+                    let coin_value = account.balance * mp;
+                    let diff = (_hold_coin - coin_value)* Decimal::from_str("0.99").unwrap();
+                    let side;
+                    let price= Decimal::ZERO;
+                    let amount;
+                    if diff > Decimal::from(20) {
+                        side = "kd";
+                        // price = mp*1.001;
+                        amount = diff/mp;
+                    } else if diff < Decimal::from(-10) {
+                        side = "kk";
+                        // price = mp*0.999;
+                        amount = -diff/mp;
+                    } else {
                         continue;
                     }
+                    info!("{}, 需要调整现货仓位 {} usdt", symbol, diff);
+                    // 价格0,市价单
+                    match self.platform_rest.take_order_symbol(symbol.clone(), Decimal::ONE, "t-123", side, price, amount).await{
+                        Ok(v)=>{
+                            info!("side: {}, {} 下单,{:?}", side, symbol, v);
+                            // 执行完当前币对  结束循环
+                            continue;
+                        },Err(ex)=>{
+                            error!("side: {}, {} {}", side, symbol, ex);
+                            // 执行完当前币对  结束循环
+                            continue;
+                        }
+                    }
+                }
+            },
+            Err(err) => {
+                error!("获取仓位信息异常: {}", err);
+            }
+        }
+    }
 
+    pub async fn check_position_swap(&mut self){
+        info!("检查遗漏仓位!");
+        match self.platform_rest.get_positions().await {
+            Ok(val)=>{
+                for position in val {
                     if position.amount.eq(&Decimal::ZERO) {
                         continue;
                     }
-
-                    match self.platform_rest.get_ticker().await {
+                    match self.platform_rest.get_ticker_symbol(position.symbol.clone()).await {
                         Ok(ticker)=>{
                             let ap = ticker.sell;
                             let bp = ticker.buy;
                             let mp = ( ap + bp ) / Decimal::TWO;
                             let price;
                             let side;
+                            let market_info;
+                            // 获取market
+                            match self.platform_rest.get_market_symbol(position.symbol.clone()).await {
+                                Ok(market) => {
+                                    market_info = market;
+                                },
+                                Err(err) => {
+                                    error!("获取当前market异常: {}", err);
+                                    continue;
+                                }
+                            }
                             info!(?position);
                             match position.position_mode {
                                 PositionModeEnum::Long => {
                                     // pd
-                                    price = (mp*dec!(0.999)/self.market.tick_size).floor()*self.market.tick_size;
+                                    price = (mp*dec!(0.999)/market_info.tick_size).floor()*market_info.tick_size;
                                     side = "pd";
                                 },
                                 PositionModeEnum::Short => {
                                     // pk
-                                    price = (mp*dec!(1.001)/self.market.tick_size).floor()*self.market.tick_size;
+                                    price = (mp*dec!(1.001)/market_info.tick_size).floor()*market_info.tick_size;
                                     side = "pk";
                                 }
                                 _ => {
                                     info!("仓位匹配失败,不做操作!");
                                     // 执行完当前币对  结束循环
-                                    break;
+                                    continue;
                                 }
                             }
-                            match self.platform_rest.take_order("t-123", side, price, position.amount.abs()).await {
+                            // 发起清仓订单
+                            match self.platform_rest.take_order_symbol(position.symbol.clone(), Decimal::ONE,"t-123", side, price, position.amount.abs()).await {
                                 Ok(order)=>{
-                                    info!("清仓下单,{:?}", order);
+                                    info!("{} 清仓下单,{:?}", position.symbol, order);
                                     // 执行完当前币对  结束循环
-                                    break;
+                                    continue;
                                 },
                                 Err(error)=>{
-                                    error!("清仓下单异常:{}", error);
+                                    error!("{} 清仓下单异常:{}", position.symbol, error);
                                     // 执行完当前币对  结束循环
-                                    break;
+                                    continue;
                                 }
                             };
                         },
@@ -1053,27 +1234,24 @@ impl Quant {
         /*
          *  停机函数
          *  mode_signal 不能小于80
-         *  前6秒用于maker平仓
-         *  后2秒用于撤maker平仓单
-         *  休眠2秒再执行check_position 避免卡单导致漏仓位
+         *  前3秒用于maker平仓
+         *  后1秒用于撤maker平仓单
+         *  休眠1秒再执行check_position 避免卡单导致漏仓位
         */
-        info!("进入停机流程...");
-        self.mode_signal = 80;
-        sleep(Duration::from_secs(10)).await;
-        
-        info!("开始退出操作");
-        info!("为避免api失效导致遗漏仓位 建议人工复查");
-        self.check_position().await;
-        // 开启停机信号
-
-        sleep(Duration::from_secs(3)).await;
-        info!("双重检查遗漏仓位");
-        self.check_position().await;
-        info!("停机退出  停机原因: {}", self.exit_msg);
-        // 发送交易状态 await self._post_params()
-        // TODO: 向中控发送信号
+        info!("止损后进入停机流程...");
         self.running.store(false, Ordering::Relaxed);
-        info!("退出进程!");
+        self.mode_signal = 80;
+        // info!("开始退出操作");
+        // info!("为避免api失效导致遗漏仓位 建议人工复查");
+        // self.check_position().await;
+        // // 开启停机信号
+        // // sleep(Duration::from_secs(1)).await;
+        // info!("双重检查遗漏仓位");
+        // self.check_position().await;
+        // info!("停机退出  停机原因: {}", self.exit_msg);
+        // // 发送交易状态 await self._post_params()
+        // // TODO: 向中控发送信号
+        // info!("退出进程!");
     }
 
     pub async fn exit(&mut self, delay: i8){
@@ -1104,12 +1282,16 @@ impl Quant {
         let ticker = self.platform_rest.get_ticker().await.expect("获取价格信息异常!");
         let mp = (ticker.buy + ticker.sell) / Decimal::TWO;
         // 获取账户信息
-        self.update_equity_rest().await;
+        if self.exchange.contains("spot") {
+            self.update_equity_rest_spot().await;
+        } else {
+            self.update_equity_rest_swap().await;
+        }
         // 初始资金
         let start_cash = self.local_cash.clone();
         let start_coin = self.local_coin.clone();
         if start_cash.is_zero() && start_coin.is_zero() {
-            self.exit_msg = format!("{}{}{}{}", "初始为零 cash: ", start_cash, " coin: ", start_coin);
+            self.exit_msg = format!("{}{}{}{}", "初始余额为零 cash: ", start_cash, " coin: ", start_coin);
             // 停止程序
             self.stop().await;
             return false;
@@ -1177,6 +1359,11 @@ impl Quant {
         self.local_cash = start_cash;
         self.local_coin = start_coin;
 
+        // 买入平台币
+        if self.exchange.contains("spot") { // 现货
+            self.buy_token().await;
+        }
+
         // 清空挂单和仓位
         self.check_position().await;
         /*
@@ -1222,9 +1409,10 @@ pub fn run_strategy(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()>{
                             let orders = quant.strategy.on_exit(&trade_msg);
                             if orders.is_not_empty() {
                                 info!("触发onExit");
+                                info!(?orders);
                                 quant._update_local_orders(&orders);
                                 spawn(async move {
-                                    platform_rest_fb.command_order(orders).await;
+                                    platform_rest_fb.command_order(orders, Default::default()).await;
                                 });
                             }
                         } else {
@@ -1232,9 +1420,11 @@ pub fn run_strategy(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()>{
                             let orders = quant.strategy.on_sleep(&trade_msg);
                             // 记录指令触发信息
                             if orders.is_not_empty() {
+                                info!("触发onSleep");
+                                info!(?orders);
                                 quant._update_local_orders(&orders);
                                 spawn(async move {
-                                    platform_rest_fb.command_order(orders).await;
+                                    platform_rest_fb.command_order(orders, Default::default()).await;
                                 });
                             }
                         }
@@ -1243,9 +1433,9 @@ pub fn run_strategy(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()>{
                     quant.check_ready();
                 }
                 // 计算耗时并进行休眠
-                let pass_time = Utc::now().timestamp_millis() - start_time;
-                if pass_time < quant.interval.to_i64().unwrap() {
-                    delay = quant.interval.to_u64().unwrap() - pass_time.to_u64().unwrap();
+                let pass_time = (Utc::now().timestamp_millis() - start_time).to_u64().unwrap();
+                if pass_time < quant.interval {
+                    delay = quant.interval - pass_time;
                 }
             }
             sleep(Duration::from_millis(delay)).await;
@@ -1280,8 +1470,6 @@ pub fn on_timer(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()> {
                 quant.strategy.trade_vol_24h_w = trade_vol_24h / dec!(10000);
                 quant.strategy.trade_vol_24h_w.rescale(2);
 
-                // 打印各类信息
-                quant.strategy._print_summary();
                 // TODO quant没有rest
                 // info!("Rest报单平均延迟{}ms", quant.rest.avg_delay);
                 // info!("Rest报单最高延迟{}ms", quant.rest.max_delay);
@@ -1293,58 +1481,33 @@ pub fn on_timer(quant_arc: Arc<Mutex<Quant>>) -> JoinHandle<()> {
     });
 }
 
-
-
-#[cfg(test)]
-mod tests {
-    use rust_decimal::Decimal;
-    use rust_decimal_macros::dec;
-    use tracing::info;
-
-    #[tokio::test]
-    async fn test_new_exchange() {
-        // let (order_tx, order_rx):(Sender<Order>, Receiver<Order>) = channel(1024);
-        // let (err_tx, err_rx):(Sender<Error>, Receiver<Error>) = channel(1024);
-        //
-        // let _params = Params::new("config.toml").unwrap();
-        //
-        // let mut params_exc: BTreeMap<String, String> = BTreeMap::new();
-        // let access_key = "";
-        // let secret_key = "";
-        //
-        // let mut quant: Quant = Quant::new(_params, params_exc,order_tx, err_tx).await;
-        // let is_ok = quant.before_trade().await;
-        //
-        // info!("结果: {}", is_ok)
-
+// 是不是不用调整仓位的币
+pub fn check_coin(exchanges :&String, coin_name: &String) -> bool{
+    let mut result = false;
+    match exchanges.as_str() {
+        "bitget_spot" => {
+            result = ["BGB", "USDT"].contains(&coin_name.as_str());
+        }
+        _ => {}
     }
+    result
+}
 
-    #[tokio::test]
-    async fn test_time(){
-        global::log_utils::init_log_with_trace();
-
-        let start_cash:Decimal = dec!(1.11);
-        let start_coin:Decimal = dec!(0.12);
-        let lever_rate:Decimal = dec!(10);
-        let grid:Decimal = dec!(1);
-        let mp:Decimal = dec!(235.562);
-        let step_size:Decimal = dec!(0.02);
-
-        let mut long_one_hand_value: Decimal = start_cash * lever_rate / grid;
-        let mut short_one_hand_value: Decimal = Decimal::ZERO;
-        let long_one_hand_amount: Decimal =(long_one_hand_value/mp/step_size).floor()*step_size;
-        let mut short_one_hand_amount: Decimal = Decimal::ZERO;
-
-        // if self.exchange.contains("spot"){
-            short_one_hand_value = start_coin * mp * lever_rate / grid;
-            short_one_hand_amount = (short_one_hand_value/mp/step_size).floor()*step_size;
-        // } else {
-        //     short_one_hand_value = start_cash * lever_rate / grid;
-        //     short_one_hand_amount = (short_one_hand_value/mp/step_size).trunc()*step_size;
-        // }
-
-        info!("long_one_hand_value:{:?}, short_one_hand_value: {:?}", long_one_hand_value, short_one_hand_value);
-        info!("long_one_hand_amount:{:?}, short_one_hand_amount: {:?}", long_one_hand_amount, short_one_hand_amount);
-        info!("{:?},{:?},{:?},{:?},{:?}", short_one_hand_value, mp, step_size, (short_one_hand_value/mp/step_size), ((short_one_hand_value/mp)/step_size).floor())
+//获取平台币
+pub fn get_exchange_token(exchanges :&String) -> TokenParam{
+    return match exchanges.as_str() {
+        "bitget_spot" => {
+            TokenParam{
+                token: "BGB".to_string(),
+                // 30u
+                limit_value: Decimal::TEN*(Decimal::ONE + Decimal::TWO)
+            }
+        }
+        _ => {
+            TokenParam{
+                token: "***".to_string(),
+                limit_value: Decimal::ZERO
+            }
+        }
     }
 }

+ 121 - 40
strategy/src/strategy.rs

@@ -7,14 +7,12 @@ 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};
+use tracing::{info, instrument, error, debug, warn};
 use global::params::Params;
 use standard::OrderCommand;
 
 #[derive(Debug)]
 pub struct Strategy {
-    // pub interval: Decimal,                                       // 原文没有使用过这个参数
-
     // 各类时间戳和时延,我们都改成了毫秒级
     pub _print_time: i64,                                           // 上次打印时间
     pub _print_interval: i64,                                       // 打印时延
@@ -25,7 +23,7 @@ pub struct Strategy {
     pub post_open_interval: i64,                                    // 提交订单时延
     pub _check_local_orders_time: i64,                              // 上次查单时间
     pub _check_local_orders_interval: i64,                          // 查单间距,原文是秒级,这里改成毫秒级
-    pub in_cancel: HashMap<String, i64>,                            //撤单队列
+    pub in_cancel: HashMap<String, i64>,                            // 撤单队列
     pub cancel_wait_interval: i64,                                  // 取消等待时延
     pub in_check: HashMap<String, i64>,                             // 查单队列
     pub check_wait_interval: i64,                                   // 检测时延
@@ -34,12 +32,13 @@ pub struct Strategy {
     pub request_limit_check_interval: i64,                          // 原文是秒级,这里改成毫秒级
     pub request_count: i64,                                         // 记录请求次数,原文的request_num
     pub request_order_count: i64,                                   // 记录下单次数,原文的request_order_num
+    pub request_over_log_interval: i64,                             // 两次超时打印之间的间隔
+    pub request_over_log_time: i64,                                 // 上次打印时间
 
     pub limit_requests_num: i64,                                    // 单位(时延)时间内请求次数上限
     pub limit_order_requests_num: i64,                              // 单位(时延)时间内下单次数上限
     pub _req_num_per_window: i64,                                   // 单位(时延)时间内请求上限窗口
 
-    pub place_order_limit: i64,                                     // 参数层面传进来的下单数量限制
     pub params: Params,                                             //
     pub exchange: String,                                           //
     pub broker_id: String,                                          //
@@ -48,7 +47,7 @@ pub struct Strategy {
     pub ref_name: Vec<String>,                                      //
     pub maker_mode: String,                                         //
     pub local_orders: HashMap<String, OrderInfo>,                   // 本地订单
-    pub pos: LocalPosition,                                              //
+    pub pos: LocalPosition,                                         //
     pub long_hold_value: Decimal,                                   //
     pub short_hold_value: Decimal,                                  //
     pub equity: Decimal,                                            //
@@ -105,10 +104,10 @@ pub struct Strategy {
 
 impl Strategy {
     pub fn new(params: &Params, is_print: bool) -> Self {
-        if params.ref_exchange.len() != params.ref_pair.len() {
-            panic!("参考盘口数不等于参考品种数,退出,请检查配置!")
+        if params.ref_exchange.len() != params.ref_pair.len(){
+            error!("参考盘口数不等于参考品种数,退出,请检查配置!");
+            panic!("参考盘口数不等于参考品种数,退出,请检查配置!");
         }
-
         // strategy的初始化,里面已经有一些参数初始化了
         let mut strategy = Self {
             _print_time: 0,
@@ -117,6 +116,8 @@ impl Strategy {
             local_start_time: 0,
             request_count: 0,
             request_order_count: 0,
+            request_over_log_interval: 60 * 1000,
+            request_over_log_time: 0,
             _print_interval: 5 * 1000,
             in_cancel: Default::default(),
             cancel_wait_interval: (0.2 * 1000f64).to_i64().unwrap(),
@@ -124,7 +125,6 @@ impl Strategy {
             check_wait_interval: 10 * 1000,
             _check_local_orders_time: 0,
             _check_local_orders_interval: 0,
-            place_order_limit: 0,
             request_limit_check_time: 0,
             request_limit_check_interval: 0,
             limit_requests_num: 0,
@@ -214,16 +214,15 @@ impl Strategy {
         strategy._check_local_orders_time = now.timestamp_millis();
         strategy._check_local_orders_interval = 10 * 1000;
         // 下单的相关限制处理
-        strategy.place_order_limit = params.place_order_limit;
         strategy.request_limit_check_time = now.timestamp_millis();
         strategy.request_limit_check_interval = 10 * 1000;
         // 求得正常请求数量和下单请求数量(interval时间内)
         let request_limit_check_interval_per_second = strategy.request_limit_check_interval / 1000;
-        strategy.limit_requests_num = utils::get_limit_requests_num_per_second(params.exchange.clone(), params.place_order_limit) * (request_limit_check_interval_per_second);
-        strategy.limit_order_requests_num = utils::get_limit_order_requests_num_per_second(params.exchange.clone(), params.place_order_limit) * (request_limit_check_interval_per_second);
+        strategy.limit_requests_num = utils::get_limit_requests_num_per_second(params.exchange.clone()) * (request_limit_check_interval_per_second);
+        strategy.limit_order_requests_num = utils::get_limit_order_requests_num_per_second(params.exchange.clone()) * (request_limit_check_interval_per_second);
         // 开仓下单间隔 均匀下单机会
         strategy.post_open_time = now.timestamp_millis();
-        let post_open_interval_per_second = Decimal::ONE.div(Decimal::from_i64(utils::get_limit_order_requests_num_per_second(params.exchange.clone(), 0)).unwrap());
+        let post_open_interval_per_second = Decimal::ONE.div(Decimal::from_i64(utils::get_limit_order_requests_num_per_second(params.exchange.clone())).unwrap());
         strategy.post_open_interval = dec!(1000).mul(post_open_interval_per_second).to_i64().unwrap();
         info!("策略模块初始化完成!");
 
@@ -341,8 +340,96 @@ impl Strategy {
     }
 
     // 打印状态信息
-    pub fn _print_summary(&self) {
-        debug!("_print_summary还没有施工……")
+    // 耗时700微秒
+    #[instrument(skip(self), level="TRACE")]
+    pub fn _print_summary(&mut self) {
+        self.mp.rescale(6);
+        self.ref_price.rescale(6);
+        self.equity.rescale(3);
+        self.cash.rescale(3);
+        let mut value = self.coin * self.mp;
+        value.rescale(3);
+        let mut price_bias = Decimal::ONE_HUNDRED * (self.ref_price - self.mp) / self.mp;
+        price_bias.rescale(2);
+
+        // 盈亏计算
+        self.profit = if self.start_equity.gt(&Decimal::ZERO) {
+            ((self.equity - self.start_equity) / self.start_equity) * Decimal::ONE_HUNDRED
+        } else {
+            Decimal::ZERO
+        };
+        self.profit.rescale(2);
+
+        // 多仓杠杆计算
+        let mut long_pos_leverage = if self.equity.gt(&Decimal::ZERO) {
+            self.pos.long_pos * self.mp / self.equity
+        } else {
+            Decimal::ZERO
+        };
+        long_pos_leverage.rescale(3);
+        // 多仓浮盈计算
+        self.long_pos_bias = if self.pos.long_pos.gt(&Decimal::ZERO) {
+            Decimal::ONE_HUNDRED - Decimal::ONE_HUNDRED * self.pos.long_avg / self.mp
+        } else {
+            Decimal::ZERO
+        };
+        self.long_pos_bias.rescale(2);
+
+        // 空仓杠杆计算
+        let mut short_pos_leverage = if self.equity.gt(&Decimal::ZERO) {
+            self.pos.short_pos * self.mp / self.equity
+        } else {
+            Decimal::ZERO
+        };
+        short_pos_leverage.rescale(3);
+        // 多仓浮盈计算
+        self.short_pos_bias = if self.pos.short_pos.gt(&Decimal::ZERO) {
+            Decimal::ONE_HUNDRED - Decimal::ONE_HUNDRED * self.pos.short_avg / self.mp
+        } else {
+            Decimal::ZERO
+        };
+        let run_time = Utc::now().timestamp_millis() - self._start_time;
+        let run_time_day = Decimal::from(run_time) / (dec!(86400000));
+        self.daily_return = self.profit / run_time_day;
+        self.daily_return.rescale(2);
+        self.short_pos_bias.rescale(2);
+        self.trade_open_dist.rescale(6);
+        self.trade_close_dist.rescale(6);
+        self.predict.rescale(5);
+        // 挂单列表长度
+        let o_num = self.local_orders.len();
+
+        let mut msg = String::new();
+        msg.push_str("当前状态: ");
+        msg.push_str(format!("[品种 {}, 现价 {:?}, 定价 {:?}, 偏差 {:?}%, 杠杆 {:?}, 动态{:?}, 最大{:?}, 预测 {:?}, 预估24H成交额 {:?}万], ",
+                             self.params.pair, self.mp, self.ref_price, price_bias, self.lever_rate,
+                             self.adjust_lever_rate, self.max_pos_rate, self.predict, self.trade_vol_24h_w).as_str());
+        msg.push_str(format!("[净值 {:?}, Cash {:?}, Coin(价值) {:?}, 日化 {:?}%], ", self.equity, self.cash, value, self.daily_return).as_str());
+        msg.push_str(format!("[推算利润 {:?}, 盈亏 {:?}%, 做多杠杆 {:?}%, 做多浮盈 {:?}%, 做空杠杆 {:?}%, 做空浮盈 {:?}%], ",
+                             self.local_profit, self.profit, long_pos_leverage, self.long_pos_bias, short_pos_leverage, self.short_pos_bias).as_str());
+        msg.push_str(format!("[请求 {:?}, 上限{:?}次/10秒], ", self._req_num_per_window, self.limit_order_requests_num).as_str());
+        msg.push_str(format!("[当前参数, 开仓 {:?}, 平仓 {:?}, 方向 {:?}, 参考 {:?}, 模式 {:?}], ",
+                             self.trade_open_dist, self.trade_close_dist, self.post_side, self.ref_name[self.ref_index], self.maker_mode).as_str());
+        msg.push_str(format!("[挂单列表,共{:?}单, ", o_num).as_str());
+        for (_, order) in &self.local_orders {
+            let mut order_value = order.amount * self.mp;
+            let mut order_lever_rate = if self.equity.gt(&Decimal::ZERO) {
+                order.amount * self.mp / self.equity
+            } else {
+                Decimal::ZERO
+            };
+            let mut order_bias = Decimal::ONE_HUNDRED * (order.price - self.mp) / self.mp;
+
+            order_value.rescale(2);
+            order_lever_rate.rescale(3);
+            order_bias.rescale(3);
+
+            msg.push_str(format!("[{:?} {:?} {:?}, 杠杆{:?}X 价值{:?}U 价格{:?} 偏离{:?}%]",
+                                 order.symbol, order.client_id, order.side, order_lever_rate, order_value, order.price, order_bias).as_str());
+        }
+
+        msg.push_str("]");
+        info!("{}", msg);
     }
 
     // 取消目标方向订单,原文是_cancel_targit_side_orders
@@ -409,7 +496,8 @@ impl Strategy {
             buy_start = mp;
             sell_start = mp;
         } else {
-            panic!("未知做市类型:mode={}", mode)
+            error!("未知做市类型:mode={}", mode);
+            panic!("未知做市类型:mode={}", mode);
         }
         debug!(?mode, ?buy_start, ?sell_start, ?mp);
 
@@ -457,37 +545,29 @@ impl Strategy {
     // 根据平均请求次数限制开仓下单
     #[instrument(skip(self, command), level="TRACE")]
     pub fn _check_request_limit(&mut self, command: &mut OrderCommand) {
-        debug!(?command);
+        let mut msg = String::new();
         // 如果当前请求数超过限制
         if self.request_count > self.limit_requests_num {
             command.cancel.clear();
             command.check.clear();
             command.limits_open.clear();
             command.limits_close.clear();
-            error!("请求频率溢出,程序禁止任何操作!");
-            debug!(?command);
-
-            return;
-        }
-
-        // 普通request超过普通请求频率上限的50%再进行判断
-        // 下单request要超过下单请求频率上限的80%再进行判断
-        if self.request_count < self.limit_requests_num * 5 / 10
-            && self.request_order_count < self.limit_order_requests_num * 8 / 10 {
-            return;
+            msg = format!("请求频率溢出,程序禁止任何操作!({}/{})", self.request_count, self.limit_requests_num);
+        } else if self.request_order_count >= self.limit_order_requests_num { // 100%超过下单频率,则不再进行平仓挂单
+            command.limits_close.clear();
+            command.limits_open.clear();
+            msg = format!("超过100%下单频率!程序禁止开平仓!({}/{})", self.request_order_count, self.limit_order_requests_num);
+        } else if self.request_count > self.limit_requests_num * 5 / 10
+            && self.request_order_count > self.limit_order_requests_num * 8 / 10 { // 超过80%,直接取消limits_open的下单指令
+            command.limits_open.clear();
+            msg = format!("超过80%下单频率,程序禁止开仓!({}/{})", self.request_order_count, self.limit_order_requests_num);
         }
 
-        // 超过80%,直接取消limits_open的下单指令
-        error!("80%+下单频率,程序禁止开仓!");
-        error!(?self.request_order_count, ?self.limit_order_requests_num);
-        command.limits_open.clear();
-        // 100%超过下单频率,则不再进行平仓挂单
-        if self.request_order_count >= self.limit_order_requests_num {
-            command.limits_close.clear();
-            error!("100%下单频率!程序禁止平仓!");
-            error!(?self.request_order_count, ?self.limit_order_requests_num)
+        // 检查是否需要打印msg
+        if !msg.is_empty() && self.local_time - self.request_over_log_time > self.request_over_log_interval {
+            warn!("{}", msg);
+            self.request_over_log_time = self.local_time;
         }
-        debug!(?command);
     }
 
     // 新增正在撤单、检查撤单队列,释放过时限制
@@ -1161,7 +1241,7 @@ mod tests {
     fn on_time_test() {
         global::log_utils::init_log_with_debug();
 
-        let params = Params::new("config.toml").unwrap();
+        let params = Params::new("config.toml.gate").unwrap();
         let mut strategy = Strategy::new(&params, true);
         let mut trader_msg = TraderMsg::new();
         trader_msg.market.append(&mut vec![dec!(0.92), dec!(1.0), dec!(0.89), dec!(0.79), dec!(0.99), dec!(1.0), dec!(0.89), dec!(0.79), dec!(0.89), dec!(0.79), dec!(0.99), dec!(1.0), dec!(0.89), dec!(0.79)]);
@@ -1180,6 +1260,7 @@ mod tests {
             create_time: 0,
             status: "".to_string(),
             fee: Default::default(),
+            trace_stack: Default::default(),
         });
         trader_msg.cash = dec!(1000);
         trader_msg.coin = Decimal::ZERO;

+ 13 - 10
strategy/src/utils.rs

@@ -2,6 +2,7 @@ use std::ops::{Div, Mul};
 use chrono::Utc;
 use rand::Rng;
 use rust_decimal::Decimal;
+use tracing::error;
 use global::public_params;
 
 // 生成订单的id,可以根据交易所名字来
@@ -43,10 +44,8 @@ pub fn fix_price(amount: Decimal, tick_size: Decimal) -> Decimal {
 }
 
 // 每秒请求频率
-pub fn get_limit_requests_num_per_second(exchange: String, limit: i64) -> i64 {
-    if limit != 0 {
-        return limit * public_params::RATIO;
-    } else if exchange.eq("kucoin_spot") {
+pub fn get_limit_requests_num_per_second(exchange: String) -> i64 {
+    if exchange.eq("kucoin_spot") {
         return public_params::KUCOIN_SPOT_LIMIT * public_params::RATIO;
     } else if exchange.eq("kucoin_usdt_swap") {
         return public_params::KUCOIN_USDT_SWAP_LIMIT * public_params::RATIO;
@@ -62,16 +61,17 @@ pub fn get_limit_requests_num_per_second(exchange: String, limit: i64) -> i64 {
         return public_params::COINEX_USDT_SWAP_LIMIT * public_params::RATIO;
     } else if exchange.eq("coinex_spot") {
         return public_params::COINEX_SPOT_LIMIT * public_params::RATIO;
+    } else if exchange.eq("bitget_spot") {
+        return public_params::BITGET_USDT_SPOT_LIMIT * public_params::RATIO;
     } else {
-        panic!("限频规则(ratio)未找到,请检查配置!")
+        error!("限频规则(ratio)未找到,请检查配置!");
+        panic!("限频规则(ratio)未找到,请检查配置!");
     }
 }
 
 // 每秒下单请求频率
-pub fn get_limit_order_requests_num_per_second(exchange: String, limit: i64) -> i64 {
-    if limit != 0 {
-        return limit
-    } else if exchange.eq("kucoin_spot") {
+pub fn get_limit_order_requests_num_per_second(exchange: String) -> i64 {
+    if exchange.eq("kucoin_spot") {
         return public_params::KUCOIN_SPOT_LIMIT
     } else if exchange.eq("kucoin_usdt_swap") {
         return public_params::KUCOIN_USDT_SWAP_LIMIT
@@ -87,8 +87,11 @@ pub fn get_limit_order_requests_num_per_second(exchange: String, limit: i64) ->
         return public_params::COINEX_USDT_SWAP_LIMIT
     } else if exchange.eq("coinex_spot") {
         return public_params::COINEX_SPOT_LIMIT
+    } else if exchange.eq("bitget_spot") {
+        return public_params::BITGET_USDT_SPOT_LIMIT
     } else {
-        panic!("限频规则(limit)未找到,请检查配置!")
+        error!("限频规则(limit)未找到,请检查配置!");
+        panic!("限频规则(limit)未找到,请检查配置!");
     }
 }
 

+ 12 - 1
strategy/tests/decimal_test.rs

@@ -1,8 +1,10 @@
 #[cfg(test)]
 mod tests {
     use std::cmp::{max, min};
+    use std::str::FromStr;
     use std::time::{Instant, Duration};
-    use rust_decimal::prelude::ToPrimitive;
+    use rust_decimal::Decimal;
+    use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
     use rust_decimal_macros::dec;
 
     #[test]
@@ -40,4 +42,13 @@ mod tests {
         println!("max({}, {})={}", a, b, max(a, b));
         println!("min({}, {})={}", a, b, min(a, b));
     }
+
+    #[test]
+    fn test_decimal_from() {
+        let a = dec!(1e-6);
+        let b = Decimal::from_f64(f64::from_str("1e-6").unwrap()).unwrap();
+
+        println!("a={}", a);
+        println!("b={:?}", b);
+    }
 }