DESKTOP-NE65RNK\Citrus_limon 1 рік тому
батько
коміт
b81d269df4

+ 305 - 580
exchanges/src/okx_swap_rest.rs

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

+ 352 - 371
exchanges/src/okx_swap_ws.rs

@@ -1,371 +1,352 @@
-// use std::sync::Arc;
-// use std::sync::atomic::AtomicBool;
-// use std::time::Duration;
-//
-// use chrono::Utc;
-// use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
-// use ring::hmac;
-// use serde_json::{json, Value};
-// use tokio::sync::Mutex;
-// use tokio_tungstenite::tungstenite::{Error, Message};
-// use tracing::{info, trace};
-//
-// use crate::response_base::ResponseData;
-// use crate::socket_tool::{AbstractWsMode, HeartbeatType};
-//
-// //类型
-// pub enum OkxSwapWsType {
-//     //订阅频道类型
-//     Public,
-//     Private,
-//     Business,
-// }
-//
-// //订阅频道
-// #[derive(Clone)]
-// pub enum OkxSwapSubscribeType {
-//     PuIndexTickers,
-//     PuBooks5,
-//     Putrades,
-//     PuBooks50L2tbt,
-//
-//     BuIndexCandle30m,
-//
-//     PrBalanceAndPosition,
-//     PrAccount(String),
-//     PrOrders,
-//     PrPositions,
-//
-// }
-//
-// //账号信息
-// #[derive(Clone)]
-// #[allow(dead_code)]
-// pub struct OkxSwapLogin {
-//     pub api_key: String,
-//     pub secret_key: String,
-//     pub passphrase: String,
-// }
-//
-// #[derive(Clone)]
-// pub struct OkxSwapWs {
-//     //类型
-//     tag: String,
-//     //地址
-//     address_url: String,
-//     //账号信息
-//     login_param: Option<OkxSwapLogin>,
-//     //币对
-//     symbol_s: Vec<String>,
-//     //订阅
-//     subscribe_types: Vec<OkxSwapSubscribeType>,
-//     //心跳间隔
-//     heartbeat_time: u64,
-// }
-//
-// impl OkxSwapWs {
-//     /*******************************************************************************************************/
-//     /*****************************************获取一个对象****************************************************/
-//     /*******************************************************************************************************/
-//     pub fn new(is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
-//         return OkxSwapWs::new_with_tag("default-OkxSwapWs".to_string(), is_colo, login_param, ws_type);
-//     }
-//     pub fn new_with_tag(tag: String, is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
-//         /*******公共频道-私有频道数据组装*/
-//         let address_url = match ws_type {
-//             OkxSwapWsType::Public => {
-//                 "wss://ws.okx.com:8443/ws/v5/public".to_string()
-//             }
-//             OkxSwapWsType::Private => {
-//                 "wss://ws.okx.com:8443/ws/v5/private".to_string()
-//             }
-//             OkxSwapWsType::Business => {
-//                 "wss://ws.okx.com:8443/ws/v5/business".to_string()
-//             }
-//         };
-//
-//         if is_colo {
-//             info!("开启高速(未配置,走普通:{})通道",address_url);
-//         } else {
-//             info!("走普通通道:{}",address_url);
-//         }
-//         /*****返回结构体*******/
-//         OkxSwapWs {
-//             tag,
-//             address_url,
-//             login_param,
-//             symbol_s: vec![],
-//             subscribe_types: vec![],
-//             heartbeat_time: 1000 * 10,
-//         }
-//     }
-//
-//     /*******************************************************************************************************/
-//     /*****************************************订阅函数********************************************************/
-//     /*******************************************************************************************************/
-//     //手动添加订阅信息
-//     pub fn set_subscribe(&mut self, subscribe_types: Vec<OkxSwapSubscribeType>) {
-//         self.subscribe_types.extend(subscribe_types);
-//     }
-//     //手动添加币对
-//     pub fn set_symbols(&mut self, mut b_array: Vec<String>) {
-//         for symbol in b_array.iter_mut() {
-//             // 小写
-//             *symbol = symbol.to_uppercase();
-//             // 字符串替换
-//             *symbol = symbol.replace("_", "-");
-//         }
-//         self.symbol_s = b_array;
-//     }
-//     //频道是否需要登录
-//     fn contains_pr(&self) -> bool {
-//         for t in self.subscribe_types.clone() {
-//             if match t {
-//                 OkxSwapSubscribeType::PuIndexTickers => false,
-//                 OkxSwapSubscribeType::PuBooks5 => false,
-//                 OkxSwapSubscribeType::Putrades => false,
-//                 OkxSwapSubscribeType::PuBooks50L2tbt => false,
-//
-//                 OkxSwapSubscribeType::BuIndexCandle30m => false,
-//
-//                 OkxSwapSubscribeType::PrBalanceAndPosition => true,
-//                 OkxSwapSubscribeType::PrAccount(_) => true,
-//                 OkxSwapSubscribeType::PrOrders => true,
-//                 OkxSwapSubscribeType::PrPositions => true,
-//             } {
-//                 return true;
-//             }
-//         }
-//         false
-//     }
-//     /*******************************************************************************************************/
-//     /*****************************************工具函数********************************************************/
-//     /*******************************************************************************************************/
-//     //订阅枚举解析
-//     pub fn enum_to_string(symbol: String, subscribe_type: OkxSwapSubscribeType) -> Value {
-//         match subscribe_type {
-//             OkxSwapSubscribeType::PuIndexTickers => {
-//                 json!({
-//                     "channel":"index-tickers",
-//                     "instId":symbol
-//                 })
-//             }
-//
-//             OkxSwapSubscribeType::PuBooks5 => {
-//                 json!({
-//                     "channel":"books5",
-//                     "instId":symbol
-//                 })
-//             }
-//             OkxSwapSubscribeType::Putrades => {
-//                 json!({
-//                     "channel":"trades",
-//                     "instId":symbol
-//                 })
-//             }
-//
-//             OkxSwapSubscribeType::BuIndexCandle30m => {
-//                 json!({
-//                     "channel":"index-candle30m",
-//                     "instId":symbol
-//                 })
-//             }
-//
-//             OkxSwapSubscribeType::PrAccount(ccy) => {
-//                 json!({
-//                     "channel":"account",
-//                     "ccy":ccy
-//                 })
-//             }
-//             OkxSwapSubscribeType::PuBooks50L2tbt => {
-//                 json!({
-//                     "channel":"books50-l2-tbt",
-//                     "instId":symbol
-//                 })
-//             }
-//             OkxSwapSubscribeType::PrBalanceAndPosition => {
-//                 json!({
-//                     "channel":"balance_and_position"
-//                 })
-//             }
-//             OkxSwapSubscribeType::PrOrders => {
-//                 json!({
-//                     "channel":"orders",
-//                     "instType":"SWAP",
-//                     "instFamily":symbol
-//                 })
-//             }
-//             OkxSwapSubscribeType::PrPositions => {
-//                 json!({
-//                     "channel":"positions",
-//                     "instType":"SWAP",
-//                 })
-//             }
-//         }
-//     }
-//     //订阅信息生成
-//     pub fn get_subscription(&self) -> String {
-//         let mut args = vec![];
-//         for symbol in &self.symbol_s {
-//             for subscribe_type in &self.subscribe_types {
-//                 let ty_str = Self::enum_to_string(symbol.clone(), subscribe_type.clone());
-//                 args.push(ty_str);
-//             }
-//         }
-//         let str = json!({
-//             "op": "subscribe",
-//             "args": args
-//         });
-//
-//         // trace!("订阅信息:{}", str.to_string());
-//
-//         str.to_string()
-//     }
-//     //登录组装
-//     fn log_in_to_str(login_param: Option<OkxSwapLogin>) -> String {
-//         let mut login_json_str = "".to_string();
-//
-//         let mut access_key: String = "".to_string();
-//         let mut secret_key: String = "".to_string();
-//         let mut passphrase: String = "".to_string();
-//
-//
-//         if let Some(param) = login_param {
-//             access_key = param.api_key;
-//             secret_key = param.secret_key;
-//             passphrase = param.passphrase;
-//         }
-//
-//         if access_key.len() > 0 || secret_key.len() > 0 || passphrase.len() > 0 {
-//             let timestamp = Utc::now().timestamp().to_string();
-//             // 时间戳 + 请求类型+ 请求参数字符串
-//             let message = format!("{}GET{}", timestamp, "/users/self/verify");
-//             let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
-//             let result = ring::hmac::sign(&hmac_key, &message.as_bytes());
-//             let sign = base64::encode(result);
-//
-//             let login_json = json!({
-//                               "op": "login",
-//                                 "args": [{
-//                                 "apiKey": access_key,
-//                                 "passphrase": passphrase,
-//                                 "timestamp": timestamp,
-//                                 "sign": sign  }]
-//                         });
-//
-//             trace!("---login_json:{0}", login_json.to_string());
-//             trace!("--登录:{}", login_json.to_string());
-//             login_json_str = login_json.to_string();
-//         }
-//         login_json_str
-//     }
-//     /*******************************************************************************************************/
-//     /*****************************************socket基本*****************************************************/
-//     /*******************************************************************************************************/
-//     //链接
-//     pub async fn ws_connect_async(&mut self,
-//                                   is_shutdown_arc: Arc<AtomicBool>,
-//                                   write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
-//                                   write_rx: UnboundedReceiver<Message>,
-//                                   read_tx: UnboundedSender<ResponseData>) -> Result<(), Error>
-//     {
-//         let login_is = self.contains_pr();
-//         let subscription = self.get_subscription();
-//         let address_url = self.address_url.clone();
-//         let tag = self.tag.clone();
-//         let heartbeat_time = self.heartbeat_time.clone();
-//
-//
-//         //心跳-- 方法内部线程启动
-//         let write_tx_clone1 = Arc::clone(write_tx_am);
-//         tokio::spawn(async move {
-//             trace!("线程-异步心跳-开始");
-//             AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time).await;
-//             trace!("线程-异步心跳-结束");
-//         });
-//
-//         //设置订阅
-//         let  subscribe_array = vec![];
-//         if login_is {
-//             let write_tx_clone2 = Arc::clone(write_tx_am);
-//             let login_str = Self::log_in_to_str(self.login_param.clone());
-//             tokio::spawn(async move {
-//                 //登录相关
-//                 AbstractWsMode::send_subscribe(write_tx_clone2, Message::Text(login_str)).await;
-//             });
-//         }
-//         let write_tx_clone3 = Arc::clone(write_tx_am);
-//         tokio::spawn(async move {
-//             tokio::time::sleep(Duration::from_millis(3 * 1000)).await;
-//             //登录相关
-//             AbstractWsMode::send_subscribe(write_tx_clone3, Message::Text(subscription)).await;
-//         });
-//
-//         //链接
-//         let t2 = tokio::spawn(async move {
-//             trace!("线程-异步链接-开始");
-//             match AbstractWsMode::ws_connect_async(is_shutdown_arc, address_url.clone(),
-//                                              tag.clone(), subscribe_array,
-//                                              write_rx, read_tx,
-//                                              Self::message_text,
-//                                              Self::message_ping,
-//                                              Self::message_pong,
-//             ).await{
-//                 Ok(_) => { trace!("线程-异步链接-结束"); }
-//                 Err(e) => { trace!("发生异常:okx-期货链接关闭-{:?}",e); }
-//             }
-//         });
-//         tokio::try_join!(t2).unwrap();
-//         trace!("线程-心跳与链接-结束");
-//
-//         Ok(())
-//     }
-//     /*******************************************************************************************************/
-//     /*****************************************数据解析*****************************************************/
-//     /*******************************************************************************************************/
-//     //数据解析-Text
-//     pub fn message_text(text: String) -> Option<ResponseData> {
-//         let  response_data = Self::ok_text(text);
-//         Option::from(response_data)
-//     }
-//     //数据解析-ping
-//     pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
-//         return Option::from(ResponseData::new("".to_string(), "-300".to_string(), "success".to_string(), "".to_string()));
-//     }
-//     //数据解析-pong
-//     pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
-//         return Option::from(ResponseData::new("".to_string(), "-301".to_string(), "success".to_string(), "".to_string()));
-//     }
-//     //数据解析
-//     pub fn ok_text(text: String) -> ResponseData
-//     {
-//         // trace!("元数据:{}",text);
-//         let mut res_data = ResponseData::new("".to_string(), "".to_string(), "success".to_string(), "".to_string());
-//         let json_value: serde_json::Value = serde_json::from_str(&text).unwrap();
-//         if json_value.get("event").is_some() {//订阅返回
-//             if json_value["event"].as_str() == Option::from("login") &&
-//                 json_value["code"].as_str() == Option::from("0") {
-//                 res_data.code = "-200".to_string();
-//                 res_data.message = format!("登录成功!");
-//             } else if json_value["event"].as_str() == Option::from("error") {
-//                 res_data.code = json_value["code"].to_string();
-//                 res_data.message = format!("订阅失败:{}", json_value["msg"].to_string());
-//             } else if json_value["event"].as_str() == Option::from("subscribe") {
-//                 res_data.code = "-201".to_string();
-//                 res_data.data = text;
-//                 res_data.message = format!("订阅成功!");
-//             }
-//         } else {
-//             if json_value.get("arg").is_some() && json_value.get("data").is_some() {
-//                 res_data.channel = format!("{}", json_value["arg"]["channel"].as_str().unwrap());
-//                 res_data.data = json_value["data"].to_string();
-//                 res_data.code = "200".to_string();
-//                 // res_data.reach_time = json_value["data"][0]["ts"].as_str().unwrap().parse().unwrap()
-//             } else {
-//                 res_data.data = text;
-//                 res_data.channel = "未知频道".to_string();
-//             }
-//         }
-//         res_data
-//     }
-// }
+use std::io::Read;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+
+use flate2::read::GzDecoder;
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use serde_json::json;
+use serde_json::Value;
+use tokio::sync::Mutex;
+use tokio_tungstenite::tungstenite::{Error, Message};
+use tracing::{error, info, trace};
+
+use crate::response_base::ResponseData;
+use crate::socket_tool::AbstractWsMode;
+
+//类型
+pub enum OkxSwapWsType {
+    Public,
+    Private,
+}
+
+
+#[derive(Debug)]
+#[derive(Clone)]
+pub struct OkxSwapWsParam {
+    pub token: String,
+    pub ws_url: String,
+    pub ws_ping_interval: i64,
+    pub ws_ping_timeout: i64,
+    pub is_ok_subscribe: bool,
+}
+
+//订阅频道
+#[derive(Clone)]
+pub enum OkxSwapSubscribeType {
+    // 深度
+    PuFuturesDepth,
+    // 公开成交
+    PuFuturesTrades,
+    // K线数据
+    PuFuturesRecords,
+}
+
+//账号信息
+#[derive(Clone, Debug)]
+pub struct OkxSwapLogin {
+    pub access_key: String,
+    pub secret_key: String,
+    pub pass_key: String,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub struct OkxSwapWs {
+    //类型
+    tag: String,
+    //地址
+    address_url: String,
+    //账号
+    login_param: Option<OkxSwapLogin>,
+    //登录数据
+    ws_param: OkxSwapWsParam,
+    //币对
+    symbol_s: Vec<String>,
+    //订阅
+    subscribe_types: Vec<OkxSwapSubscribeType>,
+    //心跳间隔
+    heartbeat_time: u64,
+}
+
+impl OkxSwapWs {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
+        return Self::new_with_tag("default-OkxSwapWs".to_string(), is_colo, login_param, ws_type);
+    }
+    pub fn new_with_tag(tag: String, is_colo: bool, login_param: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = match ws_type {
+            OkxSwapWsType::Public => {
+                let url = "wss://ws.okx.com:8443/ws/v5/public".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+            OkxSwapWsType::Private => {
+                let url = "wss://ws.okx.com:8443/ws/v5/private".to_string();
+                info!("走普通通道(不支持colo通道):{}", url);
+                url
+            }
+        };
+
+        /*******公共频道-私有频道数据组装*/
+        let ws_param = OkxSwapWsParam {
+            token: "".to_string(),
+            ws_url: "".to_string(),
+            ws_ping_interval: 0,
+            ws_ping_timeout: 0,
+            is_ok_subscribe: false,
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",address_url);
+        } else {
+            info!("走普通通道:{}",address_url);
+        }
+
+        OkxSwapWs {
+            tag,
+            address_url,
+            login_param,
+            ws_param,
+            symbol_s: vec![],
+            subscribe_types: vec![],
+            heartbeat_time: 1000 * 18,
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************订阅函数********************************************************/
+    /*******************************************************************************************************/
+    //手动添加订阅信息
+    pub fn set_subscribe(&mut self, subscribe_types: Vec<OkxSwapSubscribeType>) {
+        self.subscribe_types.extend(subscribe_types);
+    }
+    //手动添加币对
+    pub fn set_symbols(&mut self, mut b_array: Vec<String>) {
+        for symbol in b_array.iter_mut() {
+            // 大写
+            *symbol = symbol.to_uppercase();
+            // 字符串替换
+            *symbol = symbol.replace("_", "-");
+        }
+        self.symbol_s = b_array;
+    }
+    fn contains_pr(&self) -> bool {
+        for t in self.subscribe_types.clone() {
+            if match t {
+                OkxSwapSubscribeType::PuFuturesTrades => false,
+                OkxSwapSubscribeType::PuFuturesRecords => false,
+                OkxSwapSubscribeType::PuFuturesDepth => false,
+            } {
+                return true;
+            }
+        }
+        false
+    }
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    //订阅枚举解析
+    pub fn enum_to_string(symbol: String, subscribe_type: OkxSwapSubscribeType) -> Value {
+        match subscribe_type {
+            OkxSwapSubscribeType::PuFuturesDepth => {//深度
+                json!([{
+                    "channel":"books5",
+                    "instId": symbol
+                }])
+            }
+            OkxSwapSubscribeType::PuFuturesRecords => {//k线
+                json!([{
+                    "channel": "candle1s",
+                    "instId": symbol
+                }])
+            }
+            OkxSwapSubscribeType::PuFuturesTrades => {//公开成交
+                json!([{
+                    "channel":"trades",
+                    "instId": symbol
+                }])
+            }
+        }
+    }
+    //订阅信息生成
+    pub fn get_subscription(&self) -> Vec<String> {
+        let mut array = vec![];
+        for symbol in &self.symbol_s {
+            for subscribe_type in &self.subscribe_types {
+                let ty_str = Self::enum_to_string(symbol.clone(), subscribe_type.clone());
+                array.push(json!({"op": "subscribe","args": ty_str}).to_string());
+            }
+        }
+        array
+    }
+    /*******************************************************************************************************/
+    /*****************************************socket基本*****************************************************/
+    /*******************************************************************************************************/
+    //链接
+    pub async fn ws_connect_async<F, Future>(&mut self,
+                                             is_shutdown_arc: Arc<AtomicBool>,
+                                             handle_function: F,
+                                             _write_tx_am: &Arc<Mutex<UnboundedSender<Message>>>,
+                                             write_to_socket_rx: UnboundedReceiver<Message>) -> Result<(), Error>
+        where
+            F: Fn(ResponseData) -> Future + Clone + Send + 'static + Sync,
+            Future: std::future::Future<Output=()> + Send + 'static, // 确保 Fut 是一个 Future,且输出类型为 ()
+    {
+        let login_is = self.contains_pr();
+        let subscription = self.get_subscription();
+        let address_url = self.address_url.clone();
+        let tag = self.tag.clone();
+        // let heartbeat_time = self.ws_param.ws_ping_interval.clone();
+
+        //心跳-- 方法内部线程启动
+        // let write_tx_clone1 = write_tx_am.clone();
+        // tokio::spawn(async move {
+        //     trace!("线程-异步心跳-开始");
+        //     AbstractWsMode::ping_or_pong(write_tx_clone1, HeartbeatType::Ping, heartbeat_time as u64).await;
+        //     trace!("线程-异步心跳-结束");
+        // });
+
+
+        //设置订阅
+        let subscribe_array = subscription.clone();
+        if login_is {
+            //登录相关
+        }
+
+        //1 链接
+        let t2 = tokio::spawn(async move {
+            let write_to_socket_rx_arc = Arc::new(Mutex::new(write_to_socket_rx));
+
+            loop {
+                info!("Okx_usdt_swap socket 连接中……");
+                AbstractWsMode::ws_connect_async(is_shutdown_arc.clone(), handle_function.clone(), address_url.clone(),
+                                                 false, tag.clone(), subscribe_array.clone(), write_to_socket_rx_arc.clone(),
+                                                 Self::message_text, Self::message_ping, Self::message_pong, Self::message_binary).await;
+
+                error!("Okx_usdt_swap socket 断连,1s以后重连……");
+                tokio::time::sleep(Duration::from_secs(1)).await;
+            }
+        });
+
+        tokio::try_join!(t2).unwrap();
+        trace!("线程-心跳与链接-结束");
+
+        Ok(())
+    }
+    /*******************************************************************************************************/
+    /*****************************************数据解析*****************************************************/
+    /*******************************************************************************************************/
+    //数据解析-Text
+    pub fn message_text(text: String) -> Option<ResponseData> {
+        let response_data = Self::ok_text(text);
+        Option::from(response_data)
+    }
+    //数据解析-ping
+    pub fn message_ping(_pi: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -300, "success".to_string(), Value::Null));
+    }
+    //数据解析-pong
+    pub fn message_pong(_po: Vec<u8>) -> Option<ResponseData> {
+        return Option::from(ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null));
+    }
+    //数据解析-二进制
+    pub fn message_binary(po: Vec<u8>) -> Option<ResponseData> {
+        //二进制WebSocket消息
+        // let message_str = format!("Binary:{:?}", _po);
+        // Option::from(ResponseData::new("".to_string(), 2, message_str, Value::Null))
+        // let result = String::from_utf8(bytes);
+        // let result = String::from_utf8(po);
+
+        let mut gz_decoder = GzDecoder::new(&po[..]);
+        let mut decompressed_data = Vec::new();
+
+        // 尝试解压数据
+        if let Ok(_) = gz_decoder.read_to_end(&mut decompressed_data) {
+            // 将解压后的字节向量转换为 UTF-8 字符串
+            match String::from_utf8(decompressed_data) {
+                Ok(text) => {
+                    let response_data = Self::ok_text(text);
+                    return Option::from(response_data);
+                }
+                Err(_) => {
+                    return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".to_string(), Value::Null));
+                }
+            }
+        } else {
+            return Option::from(ResponseData::new("".to_string(), 400, "二进制数据转化出错".to_string(), Value::Null));
+        }
+    }
+    //数据解析
+    pub fn ok_text(text: String) -> ResponseData
+    {
+        // trace!("原始数据:{:?}",text);
+        let mut res_data = ResponseData::new("".to_string(), 200, "success".to_string(), Value::Null);
+        let json_value: Value = serde_json::from_str(&text).unwrap();
+
+        match json_value["arg"]["channel"].as_str() {
+            Some(method) => {
+                println!("------------原始数据 {}", method);
+                if method.contains("pong") {
+                    return ResponseData::new("".to_string(), -301, "success".to_string(), Value::Null);
+                } else if method.contains("") {
+                    if method.contains("books5") {
+                        res_data.channel = "futures.order_book".to_string();
+                    } else if method.contains("candle1s") {
+                        res_data.channel = "futures.candlesticks".to_string();
+                    } else if method.contains("trades") {
+                        res_data.channel = "futures.trades".to_string();
+                    } else {
+                        res_data.channel = "未知频道推送".to_string();
+                    }
+                    res_data.code = 200;
+                    res_data.data = json_value.clone();
+                } else {
+                    res_data.code = -1;
+                    res_data.message = "未知解析".to_string();
+                }
+            }
+            None => {
+                res_data.code = -1;
+                res_data.message = "未知解析".to_string();
+            }
+        }
+        //
+        // if json_value["method"].as_str() == Option::from("id1") {}
+        //
+        // // { "id": "id1", "code": 0, "msg": "" }
+        // if json_value["id"].as_str() == Option::from("id1") {
+        //     //订阅
+        //     if json_value["code"].as_i64() == Option::from(0) {
+        //         res_data.code = -201;
+        //         res_data.message = "订阅成功".to_string();
+        //     } else {
+        //         res_data.code = 400;
+        //         res_data.message = "订阅失败".to_string();
+        //     }
+        // } else if json_value["code"].as_i64() == Option::from(0) {
+        //     res_data.code = 200;
+        //     res_data.data = json_value.clone();
+        //
+        //     //订阅数据 甄别
+        //     let dataType = json_value["dataType"].as_str().unwrap();
+        //     if dataType.contains("@depth") {
+        //         res_data.channel = "futures.order_book".to_string();
+        //     } else if dataType.contains("@trade") {
+        //         res_data.channel = "futures.trades".to_string();
+        //     } else if dataType.contains("@kline_1m") {
+        //         res_data.channel = "futures.candlesticks".to_string();
+        //     } else {
+        //         res_data.channel = "未知推送数据".to_string();
+        //     }
+        // } else {
+        //     res_data.code = -1;
+        //     res_data.message = "未知解析".to_string();
+        // }
+
+        res_data
+    }
+}

+ 7 - 389
exchanges/tests/okx_swap_test.rs

@@ -1,403 +1,21 @@
 use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use futures_util::StreamExt;
-use tokio::sync::Mutex;
-use tracing::trace;
-
 use exchanges::okx_swap_rest::OkxSwapRest;
-use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+use serde_json::json;
 
 const ACCESS_KEY: &str = "";
 const SECRET_KEY: &str = "";
 const PASS_KEY: &str = "";
 
-
-//ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
-async fn ws_custom_subscribe_pu() {
-    global::log_utils::init_log_with_trace();
-
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
-
-
-    let mut ws = get_ws(None, OkxSwapWsType::Public).await;
-    ws.set_symbols(vec!["BTC_USDT".to_string()]);
-    ws.set_subscribe(vec![
-        // OkxSwapSubscribeType::PuBooks5,
-        // OkxSwapSubscribeType::Putrades,
-        OkxSwapSubscribeType::PuBooks50L2tbt,
-        // OkxSwapSubscribeType::PuIndexTickers,
-    ]);
-
-
-    let write_tx_am = Arc::new(Mutex::new(write_tx));
-    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
-
-    //读取
-    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
-    let _tr = tokio::spawn(async move {
-        trace!("线程-数据读取-开启");
-        loop {
-            if let Some(data) = read_rx.next().await {
-                trace!("读取数据data:{:?}",data)
-            }
-        }
-        // trace!("线程-数据读取-结束");
-    });
-
-    //写数据
-    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    // let write_tx_clone = Arc::clone(&write_tx_am);
-    // let su = ws.get_subscription();
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
-    //         // let close_frame = CloseFrame {
-    //         //     code: CloseCode::Normal,
-    //         //     reason: Cow::Borrowed("Bye bye"),
-    //         // };
-    //         // let message = Message::Close(Some(close_frame));
-    //
-    //
-    //         let message = Message::Text(su.clone());
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-
-//ws-订阅私有频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe_bu() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
-
-    let mut ws = get_ws(None, OkxSwapWsType::Business).await;
-    ws.set_symbols(vec!["BTC-USD".to_string()]);
-    ws.set_subscribe(vec![
-        OkxSwapSubscribeType::BuIndexCandle30m,
-    ]);
-
-    let write_tx_am = Arc::new(Mutex::new(write_tx));
-    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
-
-    //读取
-    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
-    let _tr = tokio::spawn(async move {
-        trace!("线程-数据读取-开启");
-        loop {
-            if let Some(data) = read_rx.next().await {
-                trace!("读取数据data:{:?}",data)
-            }
-        }
-        // trace!("线程-数据读取-结束");
-    });
-
-    //写数据
-    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    // let write_tx_clone = Arc::clone(&write_tx_am);
-    // let su = ws.get_subscription();
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
-    //         // let close_frame = CloseFrame {
-    //         //     code: CloseCode::Normal,
-    //         //     reason: Cow::Borrowed("Bye bye"),
-    //         // };
-    //         // let message = Message::Close(Some(close_frame));
-    //
-    //
-    //         let message = Message::Text(su.clone());
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-
-//ws-订阅私有频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
-async fn ws_custom_subscribe_pr() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
-
-    let btree_map = OkxSwapLogin {
-        api_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-        passphrase: PASS_KEY.to_string(),
-    };
-    let mut ws = get_ws(Option::from(btree_map), OkxSwapWsType::Private).await;
-    ws.set_symbols(vec!["BTC-USDT".to_string()]);
-    ws.set_subscribe(vec![
-        OkxSwapSubscribeType::PrAccount("USDT".to_string()),
-        OkxSwapSubscribeType::PrOrders,
-        OkxSwapSubscribeType::PrPositions,
-        OkxSwapSubscribeType::PrBalanceAndPosition,
-    ]);
-
-
-    let write_tx_am = Arc::new(Mutex::new(write_tx));
-    let is_shutdown_arc = Arc::new(AtomicBool::new(true));
-
-    //读取
-    let _is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
-    let _tr = tokio::spawn(async move {
-        trace!("线程-数据读取-开启");
-        loop {
-            if let Some(data) = read_rx.next().await {
-                trace!("读取数据data:{:?}",data)
-            }
-        }
-        // trace!("线程-数据读取-结束");
-    });
-
-    //写数据
-    // let bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    // let write_tx_clone = Arc::clone(&write_tx_am);
-    // let su = ws.get_subscription();
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20 * 1000)).await;
-    //         // let close_frame = CloseFrame {
-    //         //     code: CloseCode::Normal,
-    //         //     reason: Cow::Borrowed("Bye bye"),
-    //         // };
-    //         // let message = Message::Close(Some(close_frame));
-    //
-    //
-    //         let message = Message::Text(su.clone());
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), message.clone()).await;
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, &write_tx_am, write_rx, read_tx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-    //
-    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
-    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    //
-    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    // btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-    //
-    // let (tx, mut rx) = channel(1024);
-    // let mut ws = get_ws(btree_map, OkxWsType::Private, tx).await;
-    // ws.set_subscribe(vec![
-    //     OkxSubscribeType::PrBalanceAndPosition,
-    //     // OkxSubscribeType::PrAccount("USDT".to_string()),
-    //     OkxSubscribeType::PrOrders,
-    //     OkxSubscribeType::PrPositions,
-    // ]);
-    //
-    // let t1 = tokio::spawn(async move {
-    //     ws.custom_subscribe(is_shutdown_arc, vec!["BTC-USDT".to_string()]).await;
-    // });
-    //
-    // let t2 = tokio::spawn(async move {
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             trace!( "age: {:?}", received);
-    //         }
-    //     }
-    // });
-    // try_join!(t1,t2).unwrap();
-}
-
-
-//rest-订单查询
-#[tokio::test]
-async fn rest_get_order_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_order("BTC-USDT".to_string(), "3333".to_string(), "".to_string()).await;
-    println!("okx--订单查询--{:?}", req_data);
-}
-
-
-//rest-未完成的订单
+//查詢合約基礎信息
 #[tokio::test]
-async fn rest_get_incomplete_order_test() {
+async fn rest_get_market_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);
-}
+    let req_data = ret.get_market(json!({
 
-//rest-查看持仓信息
-#[tokio::test]
-async fn rest_get_positions_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_positions("SWA1P".to_string()).await;
-    println!("okx--查看持仓信息--{:?}", req_data);
-}
-
-//rest-获取单个产品行情信息
-#[tokio::test]
-async fn rest_get_ticker_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_ticker("BTC-USDT".to_string()).await;
-    println!("okx--获取单个产品行情信息--{:?}", req_data);
-}
-
-//rest-查看账户余额
-#[tokio::test]
-async fn rest_get_balance_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_balance("BTC,ETH".to_string()).await;
-    println!("okx--查看账户余额--{:?}", req_data);
-}
-
-//rest-获取交易产品基础信息
-#[tokio::test]
-async fn rest_get_instruments_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_instruments().await;
-    println!("okx--获取交易产品基础信息--{:?}", req_data);
-}
-
-//rest-获取成交明细(近三天)
-#[tokio::test]
-async fn rest_get_trade_fills_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_trade_fills("".to_string()).await;
-    println!("okx--获取成交明细(近三天)--{:?}", req_data);
-}
-
-//rest-撤单
-#[tokio::test]
-async fn rest_cancel_order_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.cancel_order("BTC-USD".to_string(), "1111".to_string(), "".to_string()).await;
-    println!("okx--撤单--{:?}", req_data);
-}
-
-//rest-设置杠杆倍数
-#[tokio::test]
-async fn rest_set_leverage_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.set_leverage("BTC-USDT".to_string(), "5".to_string()).await;
-    println!("okx--设置杠杆倍数--{:?}", req_data);
-}
-
-//rest-设置持仓模式
-#[tokio::test]
-async fn rest_set_position_mode_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.set_position_mode().await;
-    println!("okx--设置持仓模式--{:?}", req_data);
-}
-
-//rest-获取历史订单记录(近七天)
-#[tokio::test]
-async fn rest_get_orders_history_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_orders_history("".to_string(),
-                                          "".to_string(),
-                                          "filled".to_string(),
-                                          "".to_string(),
-                                          "".to_string(),
-                                          "".to_string(),
-    ).await;
-    println!("okx--获取历史订单记录--{:?}", req_data);
-}
-
-//rest-获取历史成交数据(近七天)
-#[tokio::test]
-async fn rest_get_trades_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_trades("".to_string(),
-                                  "".to_string(),
-                                  "".to_string(),
-                                  "".to_string(),
-                                  "".to_string(),
-                                  "100".to_string(),
-    ).await;
-    println!("okx--获取历史成交数据--{:?}", req_data);
-}
-
-
-async fn get_ws(btree_map: Option<OkxSwapLogin>, type_v: OkxSwapWsType) -> OkxSwapWs {
-    let ku_ws = OkxSwapWs::new(false, btree_map, type_v);
-    ku_ws
+    })).await;
+    println!("bingx--查詢合約基礎信息--{:?}", req_data);
 }
 
 fn get_rest() -> OkxSwapRest {
@@ -408,4 +26,4 @@ fn get_rest() -> OkxSwapRest {
 
     let okx_exc = OkxSwapRest::new(false, btree_map.clone());
     okx_exc
-}
+}

+ 9 - 7
src/main.rs

@@ -9,6 +9,7 @@ mod coinex_usdt_swap_data_listener;
 mod htx_usdt_swap_data_listener;
 mod bingx_usdt_swap_data_listener;
 mod mexc_usdt_swap_data_listener;
+mod okx_usdt_swap_data_listener;
 
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
@@ -33,13 +34,14 @@ async fn main() {
     // ctrl c退出检查程序
     control_c::exit_handler(running.clone());
     // 启动各交易所的数据监听器
-    binance_usdt_swap_data_listener::run_listener(running.clone()).await;
-    gate_usdt_swap_data_listener::run_listener(running.clone()).await;
-    bitget_usdt_swap_data_listener::run_listener(running.clone()).await;
-    coinex_usdt_swap_data_listener::run_listener(running.clone()).await;
-    htx_usdt_swap_data_listener::run_listener(running.clone()).await;
-    bingx_usdt_swap_data_listener::run_listener(running.clone()).await;
-    mexc_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // binance_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // gate_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // bitget_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // coinex_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // htx_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // bingx_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // mexc_usdt_swap_data_listener::run_listener(running.clone()).await;
+    okx_usdt_swap_data_listener::run_listener(running.clone()).await;
     // panic错误捕获,panic级别的错误直接退出
     // let panic_running = running.clone();
     std::panic::set_hook(Box::new(move |panic_info| {

+ 128 - 0
src/okx_usdt_swap_data_listener.rs

@@ -0,0 +1,128 @@
+use std::collections::{BTreeMap, HashMap};
+use std::str::FromStr;
+use std::sync::{Arc};
+use std::sync::atomic::AtomicBool;
+use lazy_static::lazy_static;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use tokio::sync::{Mutex};
+use tracing::info;
+use exchanges::okx_swap_rest::OkxSwapRest;
+use exchanges::okx_swap_ws::{OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
+use exchanges::response_base::ResponseData;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::json;
+use standard::exchange::ExchangeEnum;
+use standard::exchange_struct_handler::ExchangeStructHandler;
+use crate::listener_tools::{RecordMap, TradeMap, update_record, update_trade};
+
+const EXCHANGE_NAME: &str = "okx_usdt_swap";
+
+lazy_static! {
+    // static ref DEPTH_MAP: Mutex<DepthMap> = Mutex::new(HashMap::new());
+    static ref TRADES_MAP: Mutex<TradeMap> = Mutex::new(HashMap::new());
+    static ref RECORD_MAP: Mutex<RecordMap> = Mutex::new(HashMap::new());
+    static ref MUL_MAP: Mutex<HashMap<String, Decimal>> = Mutex::new(HashMap::new());
+}
+
+pub async fn run_listener(is_shutdown_arc: Arc<AtomicBool>) {
+    let name = "okx_usdt_swap_listener";
+    // 订阅所有币种
+    let login = BTreeMap::new();
+    let mut okx_rest = OkxSwapRest::new(false, login);
+    let params = json!({
+        "instType":"SWAP"
+    });
+    let response = okx_rest.get_market(params).await;
+    let mut symbols = vec![];
+    if response.code == 200 {
+        let symbol_infos = response.data.as_array().unwrap();
+        let mut mul_map = MUL_MAP.lock().await;
+        for symbol_info in symbol_infos {
+            let ct_val_ccy = symbol_info["ctValCcy"].as_str().unwrap();
+            let settle_ccy = symbol_info["settleCcy"].as_str().unwrap();
+
+            let symbol = format!("{}_{}", ct_val_ccy, settle_ccy);
+            let mul = Decimal::from_str(symbol_info["ctMult"].as_str().unwrap()).unwrap();
+            mul_map.insert(symbol.clone(), mul);
+
+            symbols.push(symbol)
+        }
+    }
+
+    for chunk in symbols.chunks(20) {
+        let ws_name = name.to_string();
+        let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        let write_tx_am = Arc::new(Mutex::new(write_tx));
+        let symbols_chunk = chunk.iter().cloned().collect::<Vec<String>>();
+        let is_shutdown_clone = Arc::clone(&is_shutdown_arc);
+
+        tokio::spawn(async move {
+            let mut ws = OkxSwapWs::new_with_tag(ws_name, false, None, OkxSwapWsType::Public);
+            ws.set_subscribe(vec![
+                OkxSwapSubscribeType::PuFuturesTrades,
+                OkxSwapSubscribeType::PuFuturesRecords,
+                // OkxSwapSubscribeType::PuFuturesOrderBook
+            ]);
+
+            // 建立链接
+            ws.set_symbols(symbols_chunk);
+            ws.ws_connect_async(is_shutdown_clone, data_listener, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        });
+    }
+}
+
+// 读取数据
+pub async fn data_listener(response: ResponseData) {
+    if response.code != 200 {
+        return;
+    }
+
+    match response.channel.as_str() {
+        // 深度数据
+        "futures.order_book" => {
+            // let depth = ExchangeStructHandler::order_book_handle(ExchangeEnum::OkxSwap, &response);
+            //
+            // update_depth(&depth).await;
+        }
+        // 订单流数据
+        "futures.trades" => {
+            println!("------------------------------- {:?}", response);
+            let mut trades = ExchangeStructHandler::trades_handle(ExchangeEnum::OkxSwap, &response);
+            let mul_map = MUL_MAP.lock().await;
+
+            for trade in trades.iter_mut() {
+                // 真实交易量处理,因为okx的量都是张数
+                let mul = mul_map[trade.symbol.as_str()];
+                let mut real_size = trade.size * mul * trade.price;
+                real_size.rescale(2);
+                trade.size = real_size;
+
+                // 更新到本地数据库
+                let trades_map = TRADES_MAP.lock().await;
+                update_trade(trade, trades_map, EXCHANGE_NAME).await;
+            }
+        }
+        // k线数据
+        "futures.candlesticks" => {
+            let mut records = ExchangeStructHandler::records_handle(ExchangeEnum::OkxSwap, &response);
+
+            let mul_map = MUL_MAP.lock().await;
+            for record in records.iter_mut() {
+                // 真实交易量处理,因为okx的量都是张数
+                let mul = mul_map[record.symbol.as_str()];
+                let mid_price = (record.high + record.low) * dec!(0.5);
+                let mut real_volume = record.volume * mul * mid_price;
+                real_volume.rescale(2);
+                record.volume = real_volume;
+
+                // 更新到本地数据库
+                let record_map = RECORD_MAP.lock().await;
+                update_record(record, record_map, EXCHANGE_NAME).await;
+            }
+        }
+        _ => {
+            info!("48 未知的数据类型: {:?}", response)
+        }
+    }
+}

+ 5 - 4
standard/src/exchange.rs

@@ -6,6 +6,7 @@ use crate::binance_swap::BinanceSwap;
 use crate::gate_swap::GateSwap;
 use crate::kucoin_swap::KucoinSwap;
 use crate::bybit_swap::BybitSwap;
+use crate::okx_swap::OkxSwap;
 use crate::bitget_swap::BitgetSwap;
 use crate::coinex_swap::CoinexSwap;
 use crate::htx_swap::HtxSwap;
@@ -27,7 +28,7 @@ pub enum ExchangeEnum {
     // GateSpot,
     KucoinSwap,
     // KucoinSpot,
-    // OkxSwap,
+    OkxSwap,
     // BitgetSpot,
     BitgetSwap,
     BybitSwap,
@@ -93,9 +94,9 @@ impl Exchange {
             // ExchangeEnum::KucoinSpot =>{
             //     Box::new(KucoinSpot::new(symbol, is_colo, params, order_sender, error_sender).await)
             // }
-            // ExchangeEnum::OkxSwap => {
-            //     Box::new(OkxSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
-            // }
+            ExchangeEnum::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)
             // }

+ 1 - 1
standard/src/lib.rs

@@ -24,7 +24,7 @@ pub mod gate_swap_handle;
 mod kucoin_swap;
 pub mod kucoin_handle;
 mod okx_swap;
-pub mod okx_handle;
+pub mod okx_swap_handle;
 mod bitget_spot;
 pub mod bitget_spot_handle;
 mod kucoin_spot;

+ 0 - 198
standard/src/okx_handle.rs

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

+ 254 - 1072
standard/src/okx_swap.rs

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

+ 43 - 0
standard/src/okx_swap_handle.rs

@@ -0,0 +1,43 @@
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::Value;
+use exchanges::response_base::ResponseData;
+use crate::{OrderBook, Trade, Record};
+
+pub fn handle_records(value: &Value) -> Vec<Record> {
+    let data = value["data"].clone();
+    return vec![Record {
+        time: Decimal::from_i64(data["t"].as_i64().unwrap() * 1000).unwrap(),
+        open: Decimal::from_f64(data["o"].as_f64().unwrap()).unwrap(),
+        high: Decimal::from_f64(data["h"].as_f64().unwrap()).unwrap(),
+        low: Decimal::from_f64(data["l"].as_f64().unwrap()).unwrap(),
+        close: Decimal::from_f64(data["c"].as_f64().unwrap()).unwrap(),
+        volume: Decimal::from_f64(data["q"].as_f64().unwrap()).unwrap(),
+        symbol: data["symbol"].as_str().unwrap().to_string(),
+    }];
+}
+
+pub fn format_depth_items(value: &Value) -> Vec<OrderBook> {
+    let mut depth_items: Vec<OrderBook> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(OrderBook {
+            price: Decimal::from_f64(value[0].as_f64().unwrap()).unwrap(),
+            amount: Decimal::from_f64(value[1].as_f64().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+pub fn format_trade_items(res_data: &ResponseData) -> Vec<Trade> {
+    let result = res_data.data["data"].clone();
+
+    let side = result["T"].as_i64().unwrap() == 1;
+    let size = Decimal::from_f64(result["v"].as_f64().unwrap()).unwrap();
+    return vec![Trade {
+        id: result["t"].to_string(),
+        time: Decimal::from_i64(result["t"].as_i64().unwrap()).unwrap(),
+        size: if side { size } else { -size },
+        price: Decimal::from_f64(result["p"].as_f64().unwrap()).unwrap(),
+        symbol: res_data.data["symbol"].as_str().unwrap().to_string(),
+    }];
+}