Răsfoiți Sursa

新增获取支持交易所的接口。

skyffire 1 an în urmă
părinte
comite
33634a714f
4 a modificat fișierele cu 1052 adăugiri și 0 ștergeri
  1. 615 0
      exchanges/src/coinex_swap_rest.rs
  2. 414 0
      exchanges/src/coinex_swap_ws.rs
  3. 2 0
      exchanges/src/lib.rs
  4. 21 0
      src/server.rs

+ 615 - 0
exchanges/src/coinex_swap_rest.rs

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

+ 414 - 0
exchanges/src/coinex_swap_ws.rs

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

+ 2 - 0
exchanges/src/lib.rs

@@ -24,4 +24,6 @@ pub mod kucoin_spot_rest;
 pub mod crypto_spot_ws;
 pub mod bybit_swap_rest;
 pub mod bybit_swap_ws;
+pub mod coinex_swap_ws;
+pub mod coinex_swap_rest;
 

+ 21 - 0
src/server.rs

@@ -165,6 +165,26 @@ async fn get_trades_count(query: web::Query<ExchangeSpecialQuery>) -> impl Respo
     }
 }
 
+#[get("/exchanges")]
+async fn get_exchanges() -> impl Responder {
+    let exchanges = vec![
+        "gate_usdt_swap",
+        "bitget_usdt_swap",
+        "binance_usdt_swap",
+    ];
+    let response_data = json!(exchanges);
+
+    let response = Response {
+        query: Value::Null,
+        msg: Some("查询成功".to_string()),
+        code: 200,
+        data: response_data,
+    };
+
+    let json_string = serde_json::to_string(&response).unwrap();
+    HttpResponse::Ok().content_type("application/json").body(json_string)
+}
+
 #[get("/get_records_map")]
 async fn get_records_map(query: web::Query<ExchangeSpecialQuery>) -> impl Responder {
     if query.validate() {
@@ -286,6 +306,7 @@ pub fn run_server(port: u32, running: Arc<AtomicBool>) {
             .service(get_symbols)
             .service(get_trades_count)
             .service(get_records_map)
+            .service(get_exchanges)
     })
     .bind(addr)
     .expect("Bind port error")