Переглянути джерело

Merge remote-tracking branch 'origin/master'

skyffire 1 рік тому
батько
коміт
fbc09d71be

+ 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 {
+    tag: 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_with_tag("default-CoinexSwapRest".to_string(), login_param);
+    }
+    pub fn new_with_tag(tag: String, login_param: BTreeMap<String, String>) -> CoinexSwapRest
+    {
+        let base_url: String = String::from("https://api.coinex.com");
+
+        /*****返回结构体*******/
+        CoinexSwapRest {
+            tag,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //获取服务器当前时间
+    pub async fn get_server_time(&mut self) -> ResponseData {
+
+        let 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.tag.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.tag.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.tag.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.tag.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
+    {
+        let mut url_and_query = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let body_s = if let Some(body) = body {
+            body
+        } else {
+            "".to_string()
+        };
+        let mut headers = HeaderMap::new();
+
+        //是否需要登陆-- 组装sing
+        if is_login {
+            // 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
+            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);
+            }
+            if !is_login_param {
+                let e = ResponseData::error(self.tag.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.tag.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.tag.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 411 - 0
exchanges/src/coinex_swap_ws.rs

@@ -0,0 +1,411 @@
+use std::io::Read;
+use std::str::from_utf8;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+use std::time::{Duration, 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};
+
+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 {
+    //类型
+    tag: 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_with_tag("default-CoinexSwapWs".to_string(), login_param);
+    }
+
+    pub fn new_with_tag(tag: String, login_param: Option<CoinexSwapLogin>) -> CoinexSwapWs
+    {
+        /*******公共频道-私有频道数据组装*/
+        let address_url = "wss://socket.coinex.com/v2/futures".to_string();
+        info!("走普通通道(不支持colo通道):{}", address_url);
+        CoinexSwapWs {
+            tag,
+            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![];
+        // 只获取第一个
+        for symbol in &self.symbol_s {
+            let symbol_final = symbol.replace("_", "").to_uppercase();
+
+            for subscribe_type in &self.subscribe_types {
+                let ty_str = Self::enum_to_string(symbol_final.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 tag = self.tag.clone();
+        let heartbeat_time = self.heartbeat_time.clone();
+
+
+        //心跳-- 方法内部线程启动
+        let write_tx_clone1 = Arc::clone(write_tx_am);
+        let write_tx_clone2 = Arc::clone(write_tx_am);
+        tokio::spawn(async move {
+            trace!("线程-异步心跳-开始");
+            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, tag.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

@@ -5,3 +5,5 @@ pub mod socket_tool;
 mod utils;
 pub mod gate_swap_ws;
 pub mod gate_swap_rest;
+pub mod coinex_swap_rest;
+pub mod coinex_swap_ws;

+ 0 - 94
exchanges/tests/binance_spot_test.rs

@@ -1,94 +0,0 @@
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-use std::time::Duration;
-use futures_util::StreamExt;
-
-use tokio::sync::Mutex;
-use tokio_tungstenite::tungstenite::Message;
-use tracing::trace;
-
-use exchanges::binance_spot_ws::{BinanceSpotLogin, BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
-use exchanges::socket_tool::AbstractWsMode;
-
-// 账号密码
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-
-
-//ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
-
-    let login_param = BinanceSpotLogin {
-        api_key: ACCESS_KEY.to_string(),
-        api_secret: SECRET_KEY.to_string(),
-    };
-    let mut ws = get_ws(None);
-    ws.set_symbols(vec!["BTC_USDT".to_string()]);
-    ws.set_subscribe(vec![
-        // BinanceSpotSubscribeType::PuBookTicker,
-        // BinanceSpotSubscribeType::PuAggTrade,
-        BinanceSpotSubscribeType::PuDepth20levels100ms,
-    ]);
-
-    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;
-}
-
-fn get_ws(login_param: Option<BinanceSpotLogin>) -> BinanceSpotWs {
-    let binance_ws = BinanceSpotWs::new(false,
-                                        login_param, BinanceSpotWsType::PublicAndPrivate,
-    );
-    binance_ws
-}
-

+ 0 - 246
exchanges/tests/binance_swap_test.rs

@@ -1,246 +0,0 @@
-use std::cmp::max;
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use tokio::sync::Mutex;
-use tracing::{info, trace};
-
-use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSwapLogin, BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
-use exchanges::response_base::ResponseData;
-use global::trace_stack::TraceStack;
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-
-
-//ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    let mut ws = get_ws(None);
-    ws.set_symbols(vec!["BTC_USDT".to_string()]);
-    ws.set_subscribe(vec![
-        BinanceSwapSubscribeType::PuBookTicker,
-        // BinanceSwapSubscribeType::PuAggTrade,
-        // BinanceSwapSubscribeType::PuDepth20levels100ms,
-    ]);
-
-
-    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!("线程-数据读取-开启");
-        let mut max_delay = 0i64;
-        loop {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(data)) = read_rx.try_next() {
-                // 消息被忽略
-                let mut trace_stack = TraceStack::new(0, Instant::now());
-                trace_stack.on_before_unlock_core();
-                trace_stack.on_after_network(data.time);
-
-                let delay = trace_stack.before_unlock_core - trace_stack.after_network;
-                max_delay = max(max_delay, delay);
-                info!("{}us, max={}us", delay, max_delay);
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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;
-
-    //************************************
-    //************************************
-    //************************************
-    //************************************
-    //************************************
-    //************************************
-    //************************************
-    //11 点31 分
-
-    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
-    // //创建读写通道
-    // let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    // let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
-    // // 封装 write_tx 到 Arc 和 Mutex
-    // let write_tx_am = Arc::new(Mutex::new(write_tx));
-    //
-    // //对象
-    // let mut ws = get_ws(None);
-    // // 币对
-    // ws.set_symbols(vec!["BTC_USDT".to_string()]);
-    // //订阅
-    // ws.set_subscribe(vec![
-    //     BinanceSwapSubscribeType::PuBookTicker,
-    //     BinanceSwapSubscribeType::PuAggTrade,
-    //     BinanceSwapSubscribeType::PuDepth20levels100ms,
-    // ]);
-    //
-    //
-    // //模拟业务场景 开启链接
-    // let is_shutdown_arc_clone = Arc::clone(&is_shutdown_arc);
-    // let write_tx_clone1 = Arc::clone(&write_tx_am);
-    // let t1 = tokio::spawn(async move {
-    //     ws.ws_connect_async(is_shutdown_arc_clone, write_tx_clone1, write_rx, &read_tx).await.unwrap();
-    //     trace!("ws_connect_async 完成");
-    // });
-    //
-    // //模拟业务场景 一直监听数据
-    // let t2 = tokio::spawn(async move {
-    //     loop {
-    //         if let Some(data) = read_rx.next().await {
-    //             trace!("读取数据data:{:?}",data)
-    //         }
-    //     }
-    //     trace!("数据读取退出 完成");
-    // });
-    //
-    //
-    // //模拟用户主动写入数据
-    // // let write_tx_clone2 = Arc::clone(&write_tx_am);
-    // // let t3 = tokio::spawn(async move {
-    // //     //模拟心跳
-    // //     loop {
-    // //         tokio::time::sleep(Duration::from_millis(5000)).await;
-    // //         let mut write_tx_clone = write_tx_clone2.lock().unwrap();
-    // //         match write_tx_clone.unbounded_send(Message::Pong(Vec::from("pong"))) {
-    // //             Ok(_) => {
-    // //                 trace!("发送心跳");
-    // //                 continue;
-    // //             }
-    // //             Err(_) => {
-    // //                 break;
-    // //             }
-    // //         }
-    // //     }
-    // //     trace!("主动推出 完成");
-    // // });
-    // // tokio::try_join!(y,y1,y2).unwrap();
-    // tokio::try_join!(t1,t2).unwrap();
-    // trace!("323123213");
-}
-
-//rest-获取服务器时间
-#[tokio::test]
-async fn rest_get_server_time_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_server_time().await;
-    trace!(?rep_data)
-}
-
-//rest-获取交易规则和交易对
-#[tokio::test]
-async fn rest_get_exchange_info_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_exchange_info().await;
-    trace!(?rep_data)
-}
-
-//rest-账户信息
-#[tokio::test]
-async fn rest_get_account_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_account().await;
-    trace!(?rep_data)
-}
-
-
-//rest-根据币对 撤销全部订单
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn rest_cancel_order_all_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-
-    let rep_data1 = rest.get_server_time().await;
-    trace!(?rep_data1);
-
-    trace!("开始时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
-    let rep_data = rest.cancel_order_all("BTCUSDT".to_string()).await;
-    trace!(?rep_data);
-    trace!("结束时间:{:?}",chrono::Utc::now().timestamp_millis().to_string());
-}
-
-//rest-账户成交历史
-#[tokio::test]
-async fn rest_get_user_trades_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_user_trades("BTCUSDT".to_string(), -1, -1, 500).await;
-    trace!(?rep_data)
-}
-
-
-fn get_ws(btree_map: Option<BinanceSwapLogin>) -> BinanceSwapWs {
-    let binance_ws = BinanceSwapWs::new(false,
-                                        btree_map,
-                                        BinanceSwapWsType::PublicAndPrivate);
-    binance_ws
-}
-
-fn get_rest() -> BinanceSwapRest {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-
-    let ba_exc = BinanceSwapRest::new(false, btree_map);
-    ba_exc
-}

+ 0 - 129
exchanges/tests/bingx_swap_test.rs

@@ -1,129 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use serde_json::json;
-use tokio::sync::Mutex;
-use tracing::trace;
-use exchanges::bingx_swap_rest::BingxSwapRest;
-
-use exchanges::bingx_swap_ws::{BingxSwapLogin, BingxSwapSubscribeType, BingxSwapWs, BingxSwapWsType};
-use exchanges::response_base::ResponseData;
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    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 {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(_)) = read_rx.try_next() {
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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 fun = move |data: ResponseData| {
-        async move {
-            // trace!("---传入的方法~~~~{:?}", data);
-        }
-    };
-    let param = BingxSwapLogin {
-        access_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-        pass_key: PASS_KEY.to_string(),
-    };
-    let t1 = tokio::spawn(async move {
-        let mut ws = get_ws(Option::from(param), BingxSwapWsType::PublicAndPrivate);
-        ws.set_symbols(vec!["BTC_USDT".to_string(), "ETC_USDT".to_string()]);
-        ws.set_subscribe(vec![
-            // BingxSwapSubscribeType::PuFuturesTrades,
-            // BingxSwapSubscribeType::PuFuturesDepth,
-            BingxSwapSubscribeType::PuFuturesRecords,
-
-        ]);
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-fn get_ws(btree_map: Option<BingxSwapLogin>, ws_type: BingxSwapWsType) -> BingxSwapWs {
-    let bingx_ws = BingxSwapWs::new(false,btree_map, ws_type);
-    bingx_ws
-}
-
-
-//查詢合約基礎信息
-#[tokio::test]
-async fn rest_get_market_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_market(json!({
-
-    })).await;
-    println!("bingx--查詢合約基礎信息--{:?}", req_data);
-}
-
-
-fn get_rest() -> BingxSwapRest {
-    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 bingx_exc = BingxSwapRest::new(false, btree_map.clone());
-    bingx_exc
-}
-

+ 0 - 515
exchanges/tests/bitget_spot_test.rs

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

+ 0 - 145
exchanges/tests/bitget_swap_test.rs

@@ -1,145 +0,0 @@
-use std::collections::BTreeMap;
-use serde_json::json;
-use tracing::{info};
-use exchanges::bitget_swap_rest::BitgetSwapRest;
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-// 测试账户信息获取
-#[tokio::test]
-async fn rest_get_account_info_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_account_info().await;
-    info!(?rep_data)
-}
-
-// 下单测试
-#[tokio::test]
-async fn post_order_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-    let params = json!({
-        "symbol": "BTCUSDT",
-        "productType": "USDT-FUTURES",
-        "marginMode": "crossed",
-        "marginCoin": "USDT",
-        "size": "0.001",
-        "side": "buy",
-        "tradeSide": "close",
-        "orderType": "market",
-        "clientOid": "1130615113245"
-    });
-    let response = rest.swap_order(params).await;
-
-    info!(?response)
-}
-
-// 撤单测试
-#[tokio::test]
-async fn cancel_order_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-    let params = json!({
-        "symbol": "ethusdt",
-        "productType": "USDT-FUTURES",
-        "clientOid": "1130615111",
-    });
-    let response = rest.cancel_order(params).await;
-
-    info!(?response)
-}
-
-// 获取订单详情测试
-#[tokio::test]
-async fn get_order_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-    let params = json!({
-        "symbol": "ETHUSDT",
-        "productType": "USDT-FUTURES",
-        "clientOid": "1130615132",
-    });
-    let response = rest.get_order(params).await;
-
-    info!(?response)
-}
-
-// 获取当前的pending订单
-#[tokio::test]
-async fn get_pending_orders_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-    let response = rest.get_pending_orders().await;
-
-    info!(?response)
-}
-
-// 设置杠杆测试
-#[tokio::test]
-async fn set_leverage_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-
-
-    let params = json!({
-        "symbol": "ETHUSDT",
-        "productType": "USDT-FUTURES",
-        "marginCoin": "USDT",
-        "leverage": "5"
-    });
-    let response = rest.set_leverage(params).await;
-
-    info!(?response)
-}
-
-// 设置持仓模式
-#[tokio::test]
-async fn set_position_mode_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-
-
-    let params = json!({
-        "productType": "USDT-FUTURES",
-        "posMode": "hedge_mode",
-    });
-    let response = rest.set_position_mode(params).await;
-
-    info!(?response)
-}
-
-// 获取仓位信息
-#[tokio::test]
-async fn get_single_position_test() {
-    global::log_utils::init_log_with_info();
-
-    let mut rest = get_rest();
-    let params = json!({
-        "productType": "USDT-FUTURES",
-        "symbol": "ETHUSDT",
-        "marginCoin": "USDT"
-    });
-    let response = rest.get_single_position(params).await;
-
-    info!(?response)
-}
-
-fn get_rest() -> BitgetSwapRest {
-    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());
-
-    BitgetSwapRest::new(false, btree_map)
-}

+ 0 - 320
exchanges/tests/bybit_swap_test.rs

@@ -1,320 +0,0 @@
-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::bybit_swap_rest::BybitSwapRest;
-use exchanges::bybit_swap_ws::{BybitSwapLogin, BybitSwapSubscribeType, BybitSwapWs, BybitSwapWsType};
-
-const ACCESS_KEY: &str = "";
-const SECRET_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, BybitSwapWsType::Public).await;
-    ws.set_symbols(vec!["BTC_USDT".to_string()]);
-    ws.set_subscribe(vec![
-        // BybitSwapSubscribeType::PuOrderBook1,
-        // BybitSwapSubscribeType::PuOrderBook50,
-        // BybitSwapSubscribeType::PuBlicTrade,
-        BybitSwapSubscribeType::PuTickers,
-    ]);
-
-
-    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 logparam = BybitSwapLogin {
-        api_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-    };
-
-    let mut ws = get_ws(Option::from(logparam), BybitSwapWsType::Private).await;
-    ws.set_symbols(vec!["BTC_USDT".to_string()]);
-    ws.set_subscribe(vec![
-        BybitSwapSubscribeType::PrPosition,
-        // BybitSwapSubscribeType::PrExecution,
-        // BybitSwapSubscribeType::PrOrder,
-        // BybitSwapSubscribeType::PrWallet,
-    ]);
-
-
-    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;
-}
-
-
-//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!("Bybit--服務器時間--{:?}", req_data);
-}
-
-//rest-查詢最新行情信息
-#[tokio::test]
-async fn rest_get_tickers_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_tickers("DOGEUSDT".to_string()).await;
-    println!("Bybit--查詢最新行情信息--{:?}", req_data);
-}
-
-//rest-查詢市場價格K線數據
-#[tokio::test]
-async fn rest_get_kline_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_kline("DOGEUSDT".to_string()).await;
-    println!("Bybit--查詢市場價格K線數據--{:?}", req_data);
-}
-
-
-//rest-查詢公告
-#[tokio::test]
-async fn rest_get_announcements_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_announcements().await;
-    println!("Bybit--查詢公告--{:?}", req_data);
-}
-
-//rest-查詢可交易產品的規格信息
-#[tokio::test]
-async fn rest_get_instruments_info_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_instruments_info("BTCUSDT".to_string()).await;
-    println!("Bybit--查詢可交易產品的規格信息--{:?}", req_data);
-}
-
-
-//rest-查詢錢包餘額
-#[tokio::test]
-async fn rest_get_account_balance_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_account_balance("USDT".to_string()).await;
-    println!("Bybit--查詢錢包餘額--{:?}", req_data);
-}
-
-//rest-查看持仓信息
-#[tokio::test]
-async fn rest_get_positions_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_positions("DOGEUSDT".to_string(), "".to_string()).await;
-    println!("Bybit--查看持仓信息--{:?}", 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("DOGEUSDT".to_string(), 3).await;
-    println!("Bybit--设置持仓模式--{:?}", 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(
-        "DOGEUSDT".to_string(), "1".to_string()).await;
-    println!("Bybit--設置槓桿--{:?}", req_data);
-}
-
-
-//rest-創建委託單
-#[tokio::test]
-async fn rest_swap_order_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let params = serde_json::json!({
-            "category":"linear",
-            "symbol":"DOGEUSDT",
-            "orderType":"Limit",
-            "side":"Buy",
-            "qty":"1",
-            "price":"0.085",
-         });
-    let req_data = ret.swap_order(params).await;
-    println!("Bybit--創建委託單--{:?}", req_data);
-}
-
-
-//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("LINKUSDT".to_string(),
-                                 "".to_string(), "".to_string()).await;
-    println!("Bybit--查詢實時委託單--{:?}", 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("DOGEUSDT".to_string(),
-                                    "1d3ea16f-cf1c-4dab-9a79-d441a2dea549".to_string(), "".to_string()).await;
-    println!("Bybit--撤单--{:?}", req_data);
-}
-
-//rest-撤銷所有訂單
-#[tokio::test]
-async fn rest_cancel_orders_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.cancel_orders("DOGEUSDT".to_string()).await;
-    println!("Bybit--撤銷所有訂單--{:?}", req_data);
-}
-
-
-async fn get_ws(btree_map: Option<BybitSwapLogin>, type_v: BybitSwapWsType) -> BybitSwapWs {
-    let ku_ws = BybitSwapWs::new(false, btree_map, type_v);
-    ku_ws
-}
-
-fn get_rest() -> BybitSwapRest {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-
-    let bybit_exc = BybitSwapRest::new(false, btree_map.clone());
-    bybit_exc
-}

+ 0 - 139
exchanges/tests/coinsph_swap_test.rs

@@ -1,139 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-use serde_json::json;
-
-use tokio::sync::Mutex;
-use tracing::trace;
-use exchanges::coinsph_swap_rest::CoinsphSwapRest;
-
-use exchanges::coinsph_swap_ws::{CoinsphSwapLogin, CoinsphSwapSubscribeType, CoinsphSwapWs, CoinsphSwapWsType};
-use exchanges::response_base::ResponseData;
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-
-
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    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 {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(_)) = read_rx.try_next() {
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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 fun = move |data: ResponseData| {
-        async move {
-            // trace!("---传入的方法~~~~{:?}", data);
-        }
-    };
-    let param = CoinsphSwapLogin {
-        api_key: "".to_string(),
-        secret: "".to_string(),
-        api_memo: "".to_string(),
-    };
-    let t1 = tokio::spawn(async move {
-        let mut ws = get_ws(Option::from(param), CoinsphSwapWsType::Public);
-        ws.set_symbols(vec!["ZRX_USDT".to_string(),"BTC_USDT".to_string(), "ETC_USDT".to_string()]);
-        ws.set_subscribe(vec![
-            CoinsphSwapSubscribeType::PuFuturesTrades,
-            CoinsphSwapSubscribeType::PuFuturesDepth,
-            CoinsphSwapSubscribeType::PuFuturesRecords,
-        ]);
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-fn get_ws(btree_map: Option<CoinsphSwapLogin>, ws_type: CoinsphSwapWsType) -> CoinsphSwapWs {
-    let coinsph_ws = CoinsphSwapWs::new(false,btree_map, ws_type);
-    coinsph_ws
-}
-
-
-
-//获取服务器时间
-#[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!("Coinsph--获取服务器时间--{:?}", req_data);
-}
-
-//获取合约信息
-#[tokio::test]
-async fn rest_get_market_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_market(json!({
-            "symbol":"ETHBTC"
-    })).await;
-    println!("Coinsph--获取合约信息--{:?}", req_data);
-}
-
-
-fn get_rest() -> CoinsphSwapRest {
-    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 Coinsph_exc = CoinsphSwapRest::new( false,btree_map.clone());
-    Coinsph_exc
-}

+ 0 - 132
exchanges/tests/cointr_swap_test.rs

@@ -1,132 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use serde_json::json;
-use tokio::sync::Mutex;
-use tracing::trace;
-use exchanges::cointr_swap_rest::CointrSwapRest;
-use exchanges::response_base::ResponseData;
-use exchanges::cointr_swap_ws::{CointrSwapLogin, CointrSwapSubscribeType, CointrSwapWs, CointrSwapWsType};
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    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 {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(_)) = read_rx.try_next() {
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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 fun = move |data: ResponseData| {
-        async move {
-            trace!("---传入的方法~~~~{:?}", data);
-        }
-    };
-    let param = CointrSwapLogin {
-        api_key: "".to_string(),
-        secret: "".to_string(),
-        api_memo: "".to_string(),
-    };
-    let t1 = tokio::spawn(async move {
-        let mut ws = get_ws(Option::from(param), CointrSwapWsType::PublicAndPrivate);
-        ws.set_symbols(vec![
-            "BTC_USDT".to_string()
-            // , "ARB_USDT".to_string(),
-            //                  "ETH_USDT".to_string(),
-        ]);
-        ws.set_subscribe(vec![
-            CointrSwapSubscribeType::PuFuturesTrades,
-            CointrSwapSubscribeType::PuFuturesDepth,
-            CointrSwapSubscribeType::PuFuturesRecords,
-        ]);
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-
-fn get_ws(btree_map: Option<CointrSwapLogin>, ws_type: CointrSwapWsType) -> CointrSwapWs {
-    let cointr_ws = CointrSwapWs::new(false, btree_map, ws_type);
-    cointr_ws
-}
-
-
-//查詢合約基礎信息
-#[tokio::test]
-async fn rest_get_market_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_market(json!({
-
-    })).await;
-    println!("Cointr--查詢合約基礎信息--{:?}", req_data);
-}
-
-
-fn get_rest() -> CointrSwapRest {
-    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 cointr_exc = CointrSwapRest::new(false, btree_map.clone());
-    cointr_exc
-}
-

+ 0 - 86
exchanges/tests/crypto_spot_test.rs

@@ -1,86 +0,0 @@
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use futures_util::StreamExt;
-use tokio::sync::Mutex;
-use tracing::trace;
-
-use exchanges::crypto_spot_ws::{CryptoSpotLogin, CryptoSpotSubscribeType, CryptoSpotWs, CryptoSpotWsType};
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-
-
-//ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
-async fn ws_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, CryptoSpotWsType::Public);
-    ws.set_symbols(vec!["BTC_USD".to_string()]);
-    ws.set_subscribe(vec![
-        // CryptoSpotSubscribeType::PuBook,
-        // CryptoSpotSubscribeType::PuTicker,
-        // CryptoSpotSubscribeType::PuTrade,
-        CryptoSpotSubscribeType::PuCandlestick,
-    ]);
-
-    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;
-}
-
-fn get_ws(btree_map: Option<CryptoSpotLogin>, ws_type: CryptoSpotWsType) -> CryptoSpotWs {
-    let binance_ws = CryptoSpotWs::new(false,
-                                       btree_map,
-                                       ws_type);
-    binance_ws
-}

+ 0 - 264
exchanges/tests/kucoin_spot_test.rs

@@ -1,264 +0,0 @@
-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::kucoin_spot_rest::KucoinSpotRest;
-use exchanges::kucoin_spot_ws::{KucoinSpotLogin, KucoinSpotSubscribeType, KucoinSpotWs, KucoinSpotWsType};
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-//ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 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, KucoinSpotWsType::Public).await;
-    ws.set_symbols(vec!["BTC-USDT".to_string()]);
-    ws.set_subscribe(vec![
-        KucoinSpotSubscribeType::PuSpotMarketLevel2Depth50,
-        KucoinSpotSubscribeType::PuMarketTicker,
-        KucoinSpotSubscribeType::PuMarketMatch,
-    ]);
-
-    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)
-            }
-        }
-    });
-
-    //写数据
-    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    let _write_tx_clone = Arc::clone(&write_tx_am);
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
-    //         let close_frame = CloseFrame {
-    //             code: CloseCode::Normal,
-    //             reason: Cow::Borrowed("Bye bye"),
-    //         };
-    //         let close_message = Message::Close(Some(close_frame));
-    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    // loop {
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&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_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 = KucoinSpotLogin {
-        access_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-        pass_key: PASS_KEY.to_string(),
-    };
-    let mut ws = get_ws(Option::from(btree_map), KucoinSpotWsType::Public).await;
-    ws.set_symbols(vec!["BTC-USDT".to_string()]);
-    ws.set_subscribe(vec![
-        KucoinSpotSubscribeType::PrAccountBalance,
-        KucoinSpotSubscribeType::PrSpotMarketTradeOrders,
-    ]);
-
-    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)
-            }
-        }
-    });
-
-    //写数据
-    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    let _write_tx_clone = Arc::clone(&write_tx_am);
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
-    //         let close_frame = CloseFrame {
-    //             code: CloseCode::Normal,
-    //             reason: Cow::Borrowed("Bye bye"),
-    //         };
-    //         let close_message = Message::Close(Some(close_frame));
-    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    // loop {
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&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;
-}
-
-
-//rest-获取成交记录
-#[tokio::test]
-async fn rest_get_fills_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_fills("BTC-USDT".to_string(),
-                                  "".to_string(),
-                                  "".to_string(),
-                                  -1,
-                                  -1,
-                                  500,
-    ).await;
-    trace!(?rep_data)
-}
-
-
-//rest-获取订单
-#[tokio::test]
-async fn rest_get_order_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_order().await;
-    trace!(?rep_data)
-}
-
-//rest-獲取行情
-#[tokio::test]
-async fn rest_get_level1_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_level1("BTC-USDT".to_string()).await;
-    trace!(?rep_data)
-}
-
-//rest-通過orderId获取訂單詳情
-#[tokio::test]
-async fn rest_get_order_by_order_id_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_order_by_order_id("3123123".to_string()).await;
-    trace!(?rep_data)
-}
-
-//rest-通過clientOid獲取訂單詳情
-#[tokio::test]
-async fn rest_get_order_by_client_id_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_order_by_client_id("3123123".to_string()).await;
-    trace!(?rep_data)
-}
-
-//rest-獲取賬戶列表 - 現貨/槓桿/現貨高頻
-#[tokio::test]
-async fn rest_get_accounts_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_accounts("USDT".to_string()).await;
-    trace!(?rep_data)
-}
-
-//rest-獲取交易對列表
-#[tokio::test]
-async fn rest_get_symbols_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.get_symbols().await;
-    trace!(?rep_data)
-}
-
-//rest-通過orderId撤單- 没权限需要查看
-#[tokio::test]
-async fn rest_cancel_order_by_order_id_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.cancel_order_by_order_id("dddd123131".to_string()).await;
-    trace!(?rep_data)
-}
-
-//rest-通過clientOid撤單- 没权限需要查看
-#[tokio::test]
-async fn rest_cancel_order_by_client_id_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut rest = get_rest();
-    let rep_data = rest.cancel_order_by_client_id("dddd123131".to_string()).await;
-    trace!(?rep_data)
-}
-
-
-async fn get_ws(btree_map: Option<KucoinSpotLogin>, type_v: KucoinSpotWsType) -> KucoinSpotWs {
-    let ku_ws = KucoinSpotWs::new(false, btree_map,
-                                  type_v).await;
-    ku_ws
-}
-
-fn get_rest() -> KucoinSpotRest {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    // btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-
-    let ku_exc = KucoinSpotRest::new(false, btree_map);
-    ku_exc
-}

+ 0 - 168
exchanges/tests/kucoin_swap_test.rs

@@ -1,168 +0,0 @@
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use futures_util::StreamExt;
-use tokio::sync::Mutex;
-use tracing::trace;
-
-use exchanges::kucoin_swap_ws::{KucoinSwapLogin, KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-//ws-订阅公共频道信息
-#[tokio::test(flavor = "multi_thread", worker_threads = 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, KucoinSwapWsType::Public).await;
-    ws.set_symbols(vec!["xbt_usdtM".to_string()]);
-    ws.set_subscribe(vec![
-        KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
-        KucoinSwapSubscribeType::PuContractMarketExecution,
-        KucoinSwapSubscribeType::PuContractMarkettickerV2,
-    ]);
-
-
-    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)
-            }
-        }
-    });
-
-    //写数据
-    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    let _write_tx_clone = Arc::clone(&write_tx_am);
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
-    //         let close_frame = CloseFrame {
-    //             code: CloseCode::Normal,
-    //             reason: Cow::Borrowed("Bye bye"),
-    //         };
-    //         let close_message = Message::Close(Some(close_frame));
-    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    // loop {
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&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 btree_map = KucoinSwapLogin {
-        access_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-        pass_key: PASS_KEY.to_string(),
-    };
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (read_tx, mut read_rx) = futures_channel::mpsc::unbounded();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    let mut ws = get_ws(Option::from(btree_map), KucoinSwapWsType::Private).await;
-    ws.set_symbols(vec!["xbt_usdtM".to_string()]);
-    ws.set_subscribe(vec![
-        // KucoinSwapSubscribeType::PuContractMarketLevel2Depth50,
-        // KucoinSwapSubscribeType::PuContractMarketExecution,
-        KucoinSwapSubscribeType::PuContractMarkettickerV2,
-        KucoinSwapSubscribeType::PrContractAccountWallet,
-        KucoinSwapSubscribeType::PrContractPosition,
-        KucoinSwapSubscribeType::PrContractMarketTradeOrdersSys,
-        KucoinSwapSubscribeType::PrContractMarketTradeOrders,
-    ]);
-
-
-    let 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)
-            }
-        }
-    });
-
-    //写数据
-    let _bool_v2_clone = Arc::clone(&is_shutdown_arc);
-    let _write_tx_clone = Arc::clone(&write_tx_am);
-    // let tw = tokio::spawn(async move {
-    //     trace!("线程-数据写入-开始");
-    //     loop {
-    //         tokio::time::sleep(Duration::from_millis(20*1000)).await;
-    //         let close_frame = CloseFrame {
-    //             code: CloseCode::Normal,
-    //             reason: Cow::Borrowed("Bye bye"),
-    //         };
-    //         let close_message = Message::Close(Some(close_frame));
-    //         // AbstractWsMode::send_subscribe(write_tx_clone.clone(), Message::Text("32313221".to_string()));
-    //         AbstractWsMode::send_subscribe(write_tx_clone.clone(), close_message);
-    //         trace!("发送指令成功");
-    //     }
-    //     trace!("线程-数据写入-结束");
-    // });
-
-    // loop {
-    let t1 = tokio::spawn(async move {
-        //链接
-        let bool_v3_clone = Arc::clone(&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;
-}
-
-
-async fn get_ws(btree_map: Option<KucoinSwapLogin>, type_v: KucoinSwapWsType) -> KucoinSwapWs {
-    let ku_ws = KucoinSwapWs::new(false, btree_map,
-                                  type_v).await;
-    ku_ws
-}

+ 0 - 128
exchanges/tests/mexc_swap_test.rs

@@ -1,128 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use serde_json::json;
-use tokio::sync::Mutex;
-use tracing::trace;
-use exchanges::mexc_swap_rest::MexcSwapRest;
-
-use exchanges::mexc_swap_ws::{MexcSwapLogin, MexcSwapSubscribeType, MexcSwapWs, MexcSwapWsType};
-use exchanges::response_base::ResponseData;
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    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 {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(_)) = read_rx.try_next() {
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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 fun = move |data: ResponseData| {
-        async move {
-            trace!("---传入的方法~~~~{:?}", data);
-        }
-    };
-    let param = MexcSwapLogin {
-        access_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-        pass_key: PASS_KEY.to_string(),
-    };
-    let t1 = tokio::spawn(async move {
-        let mut ws = get_ws(Option::from(param), MexcSwapWsType::PublicAndPrivate);
-        ws.set_symbols(vec!["BTC_USDT".to_string(), "ETC_USDT".to_string()]);
-        ws.set_subscribe(vec![
-            MexcSwapSubscribeType::PuFuturesTrades,
-            MexcSwapSubscribeType::PuFuturesDepth,
-            MexcSwapSubscribeType::PuFuturesRecords,
-        ]);
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-fn get_ws(btree_map: Option<MexcSwapLogin>, ws_type: MexcSwapWsType) -> MexcSwapWs {
-    let mexc_exc = MexcSwapWs::new(false,btree_map, ws_type);
-    mexc_exc
-}
-
-
-//查詢合約基礎信息
-#[tokio::test]
-async fn rest_get_market_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_market(json!({
-
-    })).await;
-    println!("mexc--查詢合約基礎信息--{:?}", req_data);
-}
-
-
-fn get_rest() -> MexcSwapRest {
-    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 mexc_exc = MexcSwapRest::new(false, btree_map.clone());
-    mexc_exc
-}
-

+ 0 - 127
exchanges/tests/okx_swap_test.rs

@@ -1,127 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-use exchanges::okx_swap_rest::OkxSwapRest;
-use serde_json::json;
-use tokio::sync::Mutex;
-use tracing::trace;
-use exchanges::okx_swap_ws::{OkxSwapLogin, OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
-use exchanges::response_base::ResponseData;
-
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-
-
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    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 {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(_)) = read_rx.try_next() {
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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 fun = move |data: ResponseData| {
-        async move {
-            // trace!("---传入的方法~~~~{:?}", data);
-        }
-    };
-    let param = OkxSwapLogin {
-        access_key: ACCESS_KEY.to_string(),
-        secret_key: SECRET_KEY.to_string(),
-        pass_key: PASS_KEY.to_string(),
-    };
-    let t1 = tokio::spawn(async move {
-        let mut ws = get_ws(Option::from(param), OkxSwapWsType::Public);
-        ws.set_symbols(vec!["ZRX_USDT".to_string(),"BTC_USDT".to_string(), "ETC_USDT".to_string()]);
-        ws.set_subscribe(vec![
-            OkxSwapSubscribeType::PuFuturesTrades,
-            OkxSwapSubscribeType::PuFuturesDepth,
-            // OkxSwapSubscribeType::BuFuturesRecords,
-
-        ]);
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-fn get_ws(btree_map: Option<OkxSwapLogin>, ws_type: OkxSwapWsType) -> OkxSwapWs {
-    let okx_ws = OkxSwapWs::new(false,btree_map, ws_type);
-    okx_ws
-}
-
-
-
-//查詢合約基礎信息
-#[tokio::test]
-async fn rest_get_market_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_market(json!({
-
-    })).await;
-    println!("Okx--查詢合約基礎信息--{:?}", req_data);
-}
-
-fn get_rest() -> OkxSwapRest {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), ACCESS_KEY.to_string());
-    btree_map.insert("secret_key".to_string(), SECRET_KEY.to_string());
-    btree_map.insert("pass_key".to_string(), PASS_KEY.to_string());
-
-    let okx_exc = OkxSwapRest::new(false, btree_map.clone());
-    okx_exc
-}

+ 0 - 67
exchanges/tests/socket_tool_test.rs

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

+ 0 - 547
exchanges/tests/test.rs

@@ -1,547 +0,0 @@
-use exchanges::gate_swap_rest::GateSwapRest;
-use std::collections::BTreeMap;
-use tokio::io::{AsyncReadExt};
-use exchanges::kucoin_swap_rest::KucoinSwapRest;
-use exchanges::kucoin_swap_ws::{KucoinSwapSubscribeType, KucoinSwapWs, KucoinSwapWsType};
-use exchanges::{proxy};
-use exchanges::okx_swap_ws::{OkxSwapSubscribeType, OkxSwapWs, OkxSwapWsType};
-
-use std::io::{Read, Write};
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-use tokio::sync::mpsc::channel;
-use tokio::try_join;
-use tracing::{trace};
-use tracing::instrument::WithSubscriber;
-use exchanges::binance_swap_rest::BinanceSwapRest;
-use exchanges::binance_swap_ws::{BinanceSwapSubscribeType, BinanceSwapWs, BinanceSwapWsType};
-use exchanges::okx_swap_rest::OkxSwapRest;
-
-#[tokio::test]
-async fn test_import() {
-    global::log_utils::init_log_with_trace();
-
-
-    /*******走代理:根据环境变量配置来决定,如果配置了走代理,没有配置不走*******/
-    if proxy::ParsingDetail::http_enable_proxy() {
-        trace!("检测有代理配置,配置走代理");
-    }
-
-
-    //获取代理
-    // demo_get_http_proxy();
-
-    //币安---深度socket-公共频道订阅
-    // demo_pub_ws_ba().await;
-    // 币安-rest-获取账户信息
-    // demo_rest_ba().await;
-    //本次更新成功
-
-
-    //gate-rest -账户信息
-    // demo_rest_gate().await;
-    //gate-ws-public-private频道
-    // demo_ws_gate().await;
-
-
-    //kucoin_rest -账户信息
-    // demo_rest_kucoin().await;
-    //Kucoin-ws--公共频道
-    // demo_ws_kucoin_pu().await;
-    //Kucoin-ws--私有频道
-    // demo_ws_kucoin_pr().await;
-
-    //okx - Business 频道
-    // demo_ws_okx_bu().await;
-    //okx - public 频道
-    // demo_ws_okx_pu().await;
-    //okx - rest 频道
-    // demo_okx_rest().await;
-
-    // demo_so();
-
-
-    // let mut ku_ws = KucoinSwapWs::new(false, btree_map.clone(),
-    //                                   KucoinWsType::Private, tx).await;
-    // ku_ws.set_subscribe(vec![KucoinSubscribeType::PrContractMarketTradeOrdersSys]);
-    //
-    // let t1 = tokio::spawn(async move {
-    //     ku_ws.custom_subscribe(vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
-    // });
-    //
-    // let t2 = tokio::spawn(async move {
-    //     let mut stdout = std::io::stdout();
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             writeln!(stdout, "age: {:?}", received).unwrap();
-    //         }
-    //     }
-    // let t01 = tokio::spawn(async move {
-    //     loop {
-    //         tokio::time::sleep(Duration::from_secs(2)).await;
-    //         trace!( "发送-指令: ");
-    //     }
-    // });
-    // let t02 = tokio::spawn(async move {
-    //     loop {
-    //         tokio::time::sleep(Duration::from_secs(3)).await;
-    //         trace!( "接收 -指令: ");
-    //     }
-    // });
-    // try_join!(t01,t02).unwrap();
-
-
-    // let (mut tx_end, mut rx_end) = channel::<String>(1024);
-    // let (mut tx_read, mut rx_read) = channel::<String>(1024);
-    // let mut stream = tokio::net::TcpStream::connect("127.0.0.1:8080").await.unwrap();
-    // stream.write_all( b"Hello, server!").await.unwrap();
-    // stream.flush().await.unwrap();
-    // let mutex_stream = Arc::new(Mutex::new(stream));
-    //
-    // //捕捉用户的主动发送的订阅指令
-    // let stream_clone = Arc::clone(&mutex_stream);
-    // let t_1 = tokio::spawn(async move {
-    //     loop {
-    //         tokio::time::sleep(Duration::from_secs(1)).await;
-    //         if let Ok(received) = rx_end.try_recv() {
-    //             trace!("动态订阅内容: {:?}", received);
-    //             let mut stream_lock = stream_clone.lock().await;
-    //             // stream_lock.write_all( b"1111!").await.unwrap();
-    //             // stream_lock.flush().await.unwrap();
-    //
-    //             // stream_lock.write_all(b"Hello, server!").await.unwrap();
-    //         }
-    //     }
-    // });
-    //
-    //
-    // //socket数据获取,装入回显通道
-    // let stream_clone = Arc::clone(&mutex_stream);
-    // let t_2 = tokio::spawn(async move {
-    //     // 创建一个用于存储服务器响应的缓冲区
-    //     let mut buffer = [0; 512];
-    //     loop {
-    //         let mut stream_lock = stream_clone.lock().await;
-    //         tokio::time::sleep(Duration::from_secs(1)).await;
-    //
-    //         let _ = match stream_lock.read(&mut buffer).await {
-    //             Ok(n) => {
-    //                 tokio::time::sleep(Duration::from_secs(1)).await;
-    //                 if n == 0 {
-    //                     // 没有读取到数据
-    //                     trace!("没有数据,主动发送");
-    //                 } else {
-    //                     // 打印服务器的响应
-    //                     trace!("有数据: {}", String::from_utf8_lossy(&buffer[..n]));
-    //                     tx_read.send(format!("{}", String::from_utf8_lossy(&buffer[..n]))).await.unwrap()
-    //                 }
-    //             }
-    //             Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
-    //                 // 如果读取操作会阻塞,则等待一会儿再试
-    //                 trace!("Would block, sleeping会阻碍睡眠??");
-    //                 tokio::time::sleep(Duration::from_secs(3)).await;
-    //                 continue;
-    //             }
-    //             Err(e) => {
-    //                 trace!("Err:{:?}",e);
-    //                 break;
-    //             }
-    //         };
-    //     }
-    // });
-    //
-    //
-    // //socket 数据回显
-    // let t02 = tokio::spawn(async move {
-    //     loop {
-    //         tokio::time::sleep(Duration::from_secs(1)).await;
-    //         // tx.send("hai!!!".to_string()).await.unwrap();
-    //         if let Ok(received) = rx_read.try_recv() {
-    //             trace!("拿到 socket 的数据: {:?}", received);
-    //         }
-    //     }
-    // });
-    //
-    // //模拟用户动态发送
-    // let t03 = tokio::spawn(async move {
-    //     loop {
-    //         tokio::time::sleep(Duration::from_secs(1)).await;
-    //         tx_end.send("这里是 动态订阅".to_string()).await.unwrap();
-    //     }
-    // });
-    //
-    // try_join!(t_1,t_2,t02,t03).unwrap();
-}
-
-
-async fn demo_okx_rest() {
-    // let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // btree_map.insert("access_key".to_string(), "".to_string());
-    // btree_map.insert("secret_key".to_string(), "".to_string());
-    // btree_map.insert("pass_key".to_string(), "".to_string());
-    //
-    // let mut okx_rest = OkxSwapRest::new(false, btree_map);
-    // let res_data = okx_rest.get_account("BTC".to_string()).await;
-    // trace!("okx_rest-rest - get_account- {:?}", res_data);
-}
-
-async fn demo_ws_gate() {}
-
-
-fn demo_so() {
-    // 代理服务器地址和端口
-    // let proxy_address = "127.0.0.1:7890";
-    // // 目标服务器地址和端口
-    // let target_address = "wss://ws.okx.com:8443/ws/v5/public";
-    //
-    //   // 建立与代理服务器的连接
-    //   let mut proxy_stream = TcpStream::connect(proxy_address).expect("Failed to connect to proxy");
-    //
-    //   // 发送代理请求
-    //   let request = format!("CONNECT {}\r\n\r\n", target_address);
-    //   proxy_stream.write_all(request.as_bytes()).expect("Failed to send proxy request");
-    //
-    //   // 读取代理响应
-    //   let mut response = String::new();
-    //   proxy_stream.read_to_string(&mut response).expect("Failed to read proxy response");
-    //
-    //   // 检查代理响应是否成功
-    //   if !response.contains("200 Connection established") {
-    //       trace!("Proxy connection failed: {}", response);
-    //   }
-    //
-    //   // 从代理连接中获取原始 TCP 流
-    //   let mut tcp_stream = std::mem::ManuallyDrop::new(proxy_stream);
-    //
-    //   // 现在你可以使用 `tcp_stream` 来进行与目标服务器的通信
-    //   // 例如,发送和接收数据
-    //   // tcp_stream.write_all(b"Hello, server!").expect("Failed to send data");
-    //
-    //   thread::spawn(move || {
-    //       loop {
-    //           let mut buffer = [0u8; 1024]; // 用于存储读取的数据的缓冲区
-    //           let bytes_read = tcp_stream.read(&mut buffer).expect("Failed to read data");
-    //
-    //           // 将读取的数据转换为字符串并打印
-    //           if let Ok(data) = std::str::from_utf8(&buffer[..bytes_read]) {
-    //               trace!("Received data: {}", data);
-    //           } else {
-    //               trace!("Received data contains non-UTF8 characters");
-    //           }
-    //       }
-    //
-    //   });
-    //
-    //   // 最后记得手动释放套接字
-    //   // 注意:不要调用 `drop(tcp_stream)`,因为我们使用了 `ManuallyDrop` 来避免自动释放
-    //   unsafe {
-    //       std::mem::ManuallyDrop::drop(&mut tcp_stream);
-    //   }
-}
-
-
-async fn demo_ws_okx_pu() {
-
-    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // let (tx, mut rx) = channel(1024);
-    // let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Public, tx);
-    // ku_ws.set_subscribe(vec![OkxSubscribeType::PuIndexTickers]);
-    // let t1 = tokio::spawn(async move {
-    //     ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
-    // });
-    // let t2 = tokio::spawn(async move {
-    //     let mut stdout = std::io::stdout();
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             writeln!(stdout, "age: {:?}", received).unwrap();
-    //         }
-    //     }
-    // });
-    // try_join!(t1,t2).unwrap();
-}
-
-async fn demo_ws_okx_bu() {
-    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // let (tx, mut rx) = channel(1024);
-    // let mut ku_ws = OkxSwapWs::new(false, btree_map, OkxWsType::Business, tx);
-    // ku_ws.set_subscribe(vec![OkxSubscribeType::BuIndexCandle30m]);
-    // let t1 = tokio::spawn(async move {
-    //     ku_ws.custom_subscribe(vec!["BTC-USD".to_string()]).await;
-    // });
-    // let t2 = tokio::spawn(async move {
-    //     let mut stdout = std::io::stdout();
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             writeln!(stdout, "age: {:?}", received).unwrap();
-    //         }
-    //     }
-    // });
-    // try_join!(t1,t2).unwrap();
-}
-
-async fn demo_ws_kucoin_pr() {
-    // 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(), "".to_string());
-    // btree_map.insert("secret_key".to_string(), "".to_string());
-    // btree_map.insert("pass_key".to_string(), "".to_string());
-    // trace!("----------------------btree_map{:?}", btree_map.clone());
-    // let (tx, mut rx) = channel(1024);
-    // let mut ku_ws = KucoinSwapWs::new(false, btree_map.clone(),
-    //                                   KucoinWsType::Private, tx).await;
-    // ku_ws.set_subscribe(vec![KucoinSubscribeType::PrContractMarketTradeOrdersSys]);
-    //
-    // let t1 = tokio::spawn(async move {
-    //     ku_ws.custom_subscribe(is_shutdown_arc, vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
-    // });
-    //
-    // let t2 = tokio::spawn(async move {
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             trace!( "age: {:?}", received);
-    //         }
-    //     }
-    // });
-    //
-    // try_join!(t1,t2).unwrap();
-}
-
-async fn demo_ws_kucoin_pu() {
-    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
-    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // let (tx, mut rx) = channel(1024);
-    // let mut ku_ws = KucoinSwapWs::new(false, btree_map, KucoinWsType::Public, tx).await;
-    // ku_ws.set_subscribe(vec![
-    //     KucoinSubscribeType::PuContractMarketLevel2Depth50,
-    //     KucoinSubscribeType::PuContractMarkettickerV2,
-    // ]);
-    //
-    // let t1 = tokio::spawn(async move {
-    //     ku_ws.custom_subscribe(is_shutdown_arc, vec!["ACHUSDTM".to_string(), "ROSEUSDTM".to_string()]).await;
-    // });
-    // let t2 = tokio::spawn(async move {
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             trace!("age: {:?}", received);
-    //         }
-    //     }
-    // });
-    //
-    // try_join!(t1,t2).unwrap();
-}
-
-async fn demo_rest_kucoin() {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), "".to_string());
-    btree_map.insert("secret_key".to_string(), "".to_string());
-    btree_map.insert("pass_key".to_string(), "".to_string());
-
-    let mut kucoin_exc = KucoinSwapRest::new(false, btree_map);
-    // let res_data = kucoin_exc.get_server_time().await;
-    // trace!("kucoin_exc-rest - get_server_time- {:?}", res_data);
-    // trace!("kucoin_exc-rest-get_delays{:?}", kucoin_exc.get_delays() );
-    // trace!("kucoin_exc-rest -get_avg_delay{:?}", kucoin_exc.get_avg_delay());
-    // let res_data = kucoin_exc.get_account("USDT".to_string()).await;
-    // trace!("kucoin_exc-rest - get_account- {:?}", res_data);
-    // let res_data = kucoin_exc.get_position("XBT1USDM".to_string()).await;
-    // trace!("kucoin_exc-rest - get_position- {:?}", res_data);
-    // let res_data = kucoin_exc.get_market_details().await;
-    // trace!("kucoin_exc-rest - get_market_details- {:?}", res_data);
-    // let res_data = kucoin_exc.get_ticker("ROSEUSDTM".to_string()).await;
-    // trace!("kucoin_exc-rest - get_ticker- {:?}", res_data);
-    let res_data = kucoin_exc.get_orders("".to_string(),
-                                         "".to_string()).await;
-    trace!("kucoin_exc-rest - get_orders- {:?}", res_data);
-    // let res_data = kucoin_exc.get_positions("USDT".to_string()).await;
-    // trace!("kucoin_exc-rest - get_positions- {:?}", res_data);
-    // let res_data = kucoin_exc.get_orders_details("111".to_string(), "".to_string()).await;
-    // trace!("kucoin_exc-rest - get_orders_details- {:?}", res_data);
-    // let res_data = kucoin_exc.swap_bazaar_order(
-    //     "cs_202309111808".to_string(),
-    //     "ROSEUSDTM".to_string(),
-    //     "pd".to_string(),
-    //     1,
-    //     "10".to_string(),
-    //     "0.03856".to_string(),
-    //     "limit".to_string(),
-    // ).await;
-    // trace!("kucoin_exc-rest - swap_bazaar_order- {:?}
-    // let res_data = kucoin_exc.cancel_order("".to_string(), "999999".to_string()).await;
-    // trace!("kucoin_exc-rest - cancel_order- {:?}", res_data);
-    // let res_data = kucoin_exc.get_public_token().await;
-    // trace!("kucoin_exc-rest - get_public_token- {:?}", res_data);
-}
-
-async fn demo_rest_gate() {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), "".to_string());
-    btree_map.insert("secret_key".to_string(), "".to_string());
-
-    let mut gate_exc = GateSwapRest::new(false, btree_map);
-    let res_data = gate_exc.get_account("usdt".to_string()).await;
-    trace!("gate-rest -账户信息{:?}", res_data);
-    trace!("gate-rest -get_delays{:?}", gate_exc.get_delays() );
-    trace!("gate-rest -get_avg_delay{:?}", gate_exc.get_avg_delay());
-    // let res_data = gate_exc.get_position("usdt".to_string(), "CYBER_USDT".to_string()).await;
-    // trace!("gate-rest -持仓信息{:?}", res_data);
-    // let res_data = gate_exc.get_ticker("usdt".to_string()).await;
-    // trace!("gate-rest -ticker{:?}", res_data);
-    // let res_data = gate_exc.get_server_time().await;
-    // trace!("gate-rest -get_server_time{:?}", res_data);
-    // let res_data = gate_exc.get_user_position("usdt".to_string()).await;
-    // trace!("gate-rest -get_server_time{:?}", res_data);
-    // let res_data = gate_exc.get_order_details("usdt".to_string(), "11335522".to_string()).await;
-    // trace!("gate-rest -get_order_details{:?}", res_data);
-    // let res_data = gate_exc.get_orders("usd1t".to_string(), "open".to_string()).await;
-    // trace!("gate-rest -get_orders{:?}", res_data);
-    // let params = serde_json::json!({
-    //         "contract":"CYBER_USDT",
-    //         "size":-1,
-    //         "price":"0",
-    //         "tif":"ioc",
-    //      });
-    // let res_data = gate_exc.take_order("usdt".to_string(), params).await;
-    // trace!("gate-rest -get_orders{:?}", res_data);
-    // let res_data = gate_exc.setting_dual_mode("usdt".to_string(), true).await;
-    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
-    // let res_data = gate_exc.setting_dual_leverage("usdt".to_string(),
-    //                                               "CYBER_USDT".to_string(),
-    //                                               "20".to_string(),
-    // ).await;
-    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
-    // let res_data = gate_exc.wallet_transfers("usdt".to_string(),
-    //                                               "CYBER_USDT".to_string(),
-    //                                               "20".to_string(),
-    // ).await;
-    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
-    // let res_data = gate_exc.cancel_order("usdt".to_string(),
-    //                                      "12345".to_string(),
-    // ).await;
-    // trace!("gate-rest -setting_dual_mode{:?}", res_data);
-    // let res_data = gate_exc.cancel_orders("usdt".to_string(),
-    //                                      "CYBER_USDT".to_string(),
-    // ).await;
-    // trace!("gate-rest -cancel_orders{:?}", res_data);
-    // let res_data = gate_exc.order(
-    //     "usdt".to_string(),
-    //     "long".to_string(),
-    //     "buy".to_string(),
-    //     "ROSE_USDT".to_string(),
-    //     1,
-    //     "0.03888".to_string(),
-    //     "t-my-custom-id-001".to_string(),
-    // ).await;
-    // trace!("gate-rest -order{:?}", res_data);
-
-
-    // let res_data = gate_exc.my_trades("usdt".to_string()).await;
-    // trace!("gate-rest -my_trades{:?}", res_data);
-    let res_data = gate_exc.account_book("usdt".to_string()).await;
-    trace!("gate-rest -account_book{:?}", res_data);
-}
-
-async fn demo_rest_ba() {
-    let mut btree_map: BTreeMap<String, String> = BTreeMap::new();
-    btree_map.insert("access_key".to_string(), "".to_string());
-    btree_map.insert("secret_key".to_string(), "".to_string());
-
-    // let ba_exc = BinanceUsdtSwapRest::new(false, btree_map);
-    // let res_data = ba_exc.get_account().await;
-    let mut ba_exc = BinanceSwapRest::new(false, btree_map);
-    // let res_data = ba_exc.get_server_time().await;
-    // trace!("币安-rest - get_server_time{:?}", res_data);
-    // let res_data = ba_exc.get_exchange_info().await;
-    // trace!("币安-restget_ get_exchange_info{:?}", res_data);
-
-
-    let res_data = ba_exc.get_account().await;
-    trace!("币安-rest-获取账户信息{:?}", res_data);
-    // trace!("币安-rest- -get_delays{:?}", ba_exc.get_delays() );
-    // trace!("币安-rest--get_avg_delay{:?}", ba_exc.get_avg_delay());
-
-    // let res_data = ba_exc.get_order("BTCUSDT".to_string(), 131, "".to_string()).await;
-    // trace!("币安-rest--get_order{:?}", res_data);
-    // let res_data = ba_exc.get_open_orders("BTCUSDT".to_string()).await;
-    // trace!("币安-rest--get_open_orders{:?}", res_data);
-    // let timestamp_start = chrono::Utc::now().timestamp_millis() - 60 * 100 * 1000;
-    // let timestamp_end = chrono::Utc::now().timestamp_millis() +  60 * 100 * 1000;
-    // let res_data = ba_exc.get_all_orders("BTCUSDT".to_string(), 1000, timestamp_start, timestamp_end).await;
-    // trace!("币安-rest--get_all_orders{:?}", res_data);
-    // let res_data = ba_exc.get_book_ticker("BTCUSDT".to_string()).await;
-    // trace!("币安-rest--get_book_ticker{:?}", res_data);
-    // let res_data = ba_exc.get_position_risk("BTCUS1DT".to_string()).await;
-    // trace!("币安-rest--get_position_risk{:?}", res_data);
-    // let res_data = ba_exc.change_pos_side(true).await;
-    // trace!("币安-rest--change_pos_side{:?}", res_data);
-    // let res_data = ba_exc.cancel_order("BTCUSDT".to_string(), 3312331, "".to_string()).await;
-    // trace!("币安-rest--cancel_order{:?}", res_data);
-}
-
-async fn demo_pub_ws_ba() {
-    // let mut is_shutdown_arc = Arc::new(AtomicBool::new(true));
-    // let btree_map: BTreeMap<String, String> = BTreeMap::new();
-    // let (tx, mut rx) = channel(1024);
-    // let mut binance_ws = BinanceSwapWs::new(false, btree_map, BinanceWsType::PublicAndPrivate, tx);
-    // binance_ws.set_subscribe(vec![
-    //     BinanceSubscribeType::PuAggTrade,
-    //     BinanceSubscribeType::PuDepth20levels100ms,
-    //     BinanceSubscribeType::PuBookTicker,
-    // ]);
-    // let t1 = tokio::spawn(async move {
-    //     binance_ws.custom_subscribe(is_shutdown_arc,vec!["BTCUSDT".to_string()]).await;
-    // });
-    // let t2 = tokio::spawn(async move {
-    //     loop {
-    //         if let Ok(received) = rx.try_recv() {
-    //             trace!( "age: {:?}", received);
-    //         }
-    //     }
-    // });
-    // try_join!(t1,t2).unwrap();
-}
-
-fn demo_get_http_proxy() {
-    //代理地址
-    let parsing_detail = proxy::ParsingDetail::parsing_environment_variables();
-    trace!("----代理信息:{:?}", parsing_detail);
-}
-
-
-// /*********************web服务*/
-// fn demo_http() {
-//     let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
-//     for stream in listener.incoming() {
-//         let stream = stream.unwrap();
-//
-//         handle_connection(TcpStream::try_from(stream).unwrap());
-//     }
-// }
-//
-//
-// fn handle_connection(mut stream: TcpStream) {
-//     let buf_reader = BufReader::new(&mut stream);
-//     let http_request: Vec<_> = buf_reader
-//         .lines()
-//         .map(|result| result.unwrap())
-//         .take_while(|line| !line.is_empty())
-//         .collect();
-//     trace!("Request: {:#?}", http_request);
-//     trace!("Request2: {:#?}", http_request[0]);
-//     trace!("Request3: {:#?}", http_request[1]);
-//
-//     let (status_line, filename) = if http_request[0] == "GET / HTTP/1.1" {
-//         ("HTTP/1.1 200 OK", "hello.html")
-//     } else {
-//         ("HTTP/1.1 404 NOT FOUND", "404.html")
-//     };
-//
-//     let status_line = "HTTP/1.1 200 OK";
-//     let contents = fs::read_to_string("src/404.html").unwrap();
-//     let length = contents.len();
-//
-//     let response =
-//         format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
-//     // let response = "HTTP/1.1 200 OK\r\n\r\nccccc";
-//
-//     stream.write_all(response.as_bytes()).unwrap();
-// }

+ 0 - 140
exchanges/tests/woo_swap_test.rs

@@ -1,140 +0,0 @@
-use std::collections::BTreeMap;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-use serde_json::json;
-use tokio::sync::Mutex;
-use tracing::trace;
-use exchanges::response_base::ResponseData;
-use exchanges::woo_swap_rest::WooSwapRest;
-use exchanges::woo_swap_ws::{WooSwapLogin, WooSwapSubscribeType, WooSwapWs, WooSwapWsType};
-
-const APPLICATION_ID: &str = "4bd2d1a1-c033-43a1-b977-1aefa754e715";
-const ACCESS_KEY: &str = "";
-const SECRET_KEY: &str = "";
-const PASS_KEY: &str = "";
-
-
-#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn ws_custom_subscribe() {
-    global::log_utils::init_log_with_trace();
-
-    let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
-    let (_, mut read_rx) = futures_channel::mpsc::unbounded::<ResponseData>();
-
-    // let (write_tx, write_rx) = tokio::sync::broadcast::channel::<Message>(10);
-    // let (read_tx, mut read_rx) = tokio::sync::broadcast::channel::<ResponseData>(10);
-
-
-    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 {
-            // 从通道中接收并丢弃所有的消息,直到通道为空
-            while let Ok(Some(_)) = read_rx.try_next() {
-
-                // 从通道中接收并丢弃所有的消息,直到通道为空
-                while let Ok(Some(_)) = read_rx.try_next() {
-                    // 消息被忽略
-                }
-            }
-        }
-        // 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 fun = move |data: ResponseData| {
-        async move {
-            trace!("---传入的方法~~~~{:?}", data);
-        }
-    };
-    let param = WooSwapLogin {
-        api_key: "".to_string(),
-        secret: "".to_string(),
-        api_memo: "".to_string(),
-    };
-    let t1 = tokio::spawn(async move {
-        let mut ws = get_ws(Option::from(param), WooSwapWsType::Public(APPLICATION_ID.to_string()));
-        ws.set_symbols(vec!["BTC_USDT".to_string(),"ETC_USDT".to_string()]);
-        ws.set_subscribe(vec![
-            WooSwapSubscribeType::PuFuturesTrades,
-            WooSwapSubscribeType::PuFuturesDepth,
-            WooSwapSubscribeType::PuFuturesRecords,
-        ]);
-        //链接
-        let bool_v3_clone = Arc::clone(&is_shutdown_arc);
-        ws.ws_connect_async(bool_v3_clone, fun, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
-        trace!("test 唯一线程结束--");
-    });
-    tokio::try_join!(t1).unwrap();
-    trace!("当此结束");
-    trace!("重启!");
-    trace!("参考交易所关闭");
-    return;
-}
-
-fn get_ws(btree_map: Option<WooSwapLogin>, ws_type: WooSwapWsType) -> WooSwapWs {
-    let Woo_ws = WooSwapWs::new(false, btree_map, ws_type);
-    Woo_ws
-}
-
-
-//查询服务器时间是
-#[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!("Woo--查询服务器时间是--{:?}", req_data);
-}
-
-
-//查詢合約基礎信息
-#[tokio::test]
-async fn rest_get_market_test() {
-    global::log_utils::init_log_with_trace();
-
-    let mut ret = get_rest();
-    let req_data = ret.get_market(json!({
-
-    })).await;
-    println!("Woo--查詢合約基礎信息--{:?}", req_data);
-}
-
-
-fn get_rest() -> WooSwapRest {
-    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 woo_exc = WooSwapRest::new(false, btree_map.clone());
-    woo_exc
-}
-

+ 114 - 0
src/coinex_usdt_swap_data_listener.rs

@@ -0,0 +1,114 @@
+
+use std::collections::{BTreeMap, HashMap};
+use std::sync::{Arc};
+use std::sync::atomic::AtomicBool;
+use std::time::Duration;
+use chrono::Utc;
+use lazy_static::lazy_static;
+use tokio::sync::{Mutex};
+use tracing::info;
+use exchanges::coinex_swap_rest::CoinexSwapRest;
+use exchanges::coinex_swap_ws::{CoinexSwapSubscribeType, CoinexSwapWs};
+use exchanges::response_base::ResponseData;
+use rust_decimal_macros::dec;
+use standard::exchange::ExchangeEnum;
+use standard::exchange_struct_handler::ExchangeStructHandler;
+use crate::json_db_utils::{collect_special_trades_json, delete_db_by_exchange};
+use crate::listener_tools::{TradeMap, update_trade};
+use crate::msv::{generate_msv_by_trades, Indicators, parse_json_to_trades};
+
+const EXCHANGE_NAME: &str = "coinex_usdt_swap";
+
+lazy_static! {
+    // 给其他模块使用
+    pub static ref INDICATOR_MAP: Mutex<HashMap<String, Indicators>> = Mutex::new(HashMap::new());
+
+    static ref TRADES_MAP: Mutex<TradeMap> = Mutex::new(HashMap::new());
+}
+
+pub async fn run_listener(is_shutdown_arc: Arc<AtomicBool>) {
+    let name = "coinex_usdt_swap_listener";
+    // 订阅所有币种
+    let login = BTreeMap::new();
+    let mut coinex_rest = CoinexSwapRest::new(login);
+    let response = coinex_rest.get_market_details("usdt".to_string()).await;
+    let mut symbols = vec![];
+    if response.code == 200 {
+        let data = response.data.as_array().unwrap();
+        for info in data {
+            let s = info["market"].as_str().unwrap();
+            if !s.ends_with("USDT") { continue; }
+            let symbol = s.to_string().replace("USDT", "_USDT");
+            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 = CoinexSwapWs::new_with_tag(ws_name, None);
+            ws.set_subscribe(vec![
+                CoinexSwapSubscribeType::PuFuturesDeals,
+                // GateSwapSubscribeType::PuFuturesOrderBook
+            ]);
+
+            // 建立链接
+            ws.set_symbols(symbols_chunk);
+            ws.ws_connect_async(is_shutdown_clone, data_listener, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        });
+    }
+    // 每分钟计算msv
+    tokio::spawn(async move {
+        loop {
+            let end_timestamp = Utc::now().timestamp_millis();
+            let start_timestamp = end_timestamp - 60 * 1000 * 60;
+            for symbol in symbols.clone() {
+                let trades_value = collect_special_trades_json(start_timestamp, end_timestamp, EXCHANGE_NAME, &symbol).await;
+                let trades = parse_json_to_trades(trades_value);
+                let msv = generate_msv_by_trades(trades, dec!(50), vec![], start_timestamp, end_timestamp);
+                let mut indicator_map = INDICATOR_MAP.lock().await;
+                indicator_map.insert(symbol, msv);
+            }
+            tokio::time::sleep(Duration::from_secs(60)).await;
+        }
+    });
+    // 定时删除数据
+    tokio::spawn(async move {
+        loop {
+            delete_db_by_exchange(EXCHANGE_NAME, "trades", 5 * 60).await;
+            tokio::time::sleep(Duration::from_secs(3600)).await;
+        }
+    });
+}
+
+// 读取数据
+pub async fn data_listener(response: ResponseData) {
+    if response.code != 200 {
+        return;
+    }
+
+    match response.channel.as_str() {
+        // 订单流数据
+        "deals.update" => {
+            let mut trades = ExchangeStructHandler::trades_handle(ExchangeEnum::CoinexSwap, &response);
+
+            for trade in trades.iter_mut() {
+                // 更新为计价币的量
+                trade.size = trade.price * trade.size;
+                trade.size.rescale(2);
+
+                // 订单流数据更新
+                let trades_map_1 = TRADES_MAP.lock().await;
+                update_trade(trade, trades_map_1, EXCHANGE_NAME).await;
+            }
+        }
+        _ => {
+            info!("85 未知的数据类型: {:?}", response)
+        }
+    }
+}

+ 2 - 2
src/gate_usdt_swap_data_listener.rs

@@ -72,7 +72,7 @@ pub async fn run_listener(is_shutdown_arc: Arc<AtomicBool>) {
             let end_timestamp = Utc::now().timestamp_millis();
             let start_timestamp = end_timestamp - 60 * 1000 * 60;
             for symbol in symbols.clone() {
-                let trades_value = collect_special_trades_json(start_timestamp, end_timestamp, "gate_usdt_swap", &symbol).await;
+                let trades_value = collect_special_trades_json(start_timestamp, end_timestamp, EXCHANGE_NAME, &symbol).await;
                 let trades = parse_json_to_trades(trades_value);
                 let msv = generate_msv_by_trades(trades, dec!(50), vec![], start_timestamp, end_timestamp);
                 let mut indicator_map = INDICATOR_MAP.lock().await;
@@ -84,7 +84,7 @@ pub async fn run_listener(is_shutdown_arc: Arc<AtomicBool>) {
     // 定时删除数据
     tokio::spawn(async move {
         loop {
-            delete_db_by_exchange("gate_usdt_swap", "trades", 5 * 60).await;
+            delete_db_by_exchange(EXCHANGE_NAME, "trades", 5 * 60).await;
             tokio::time::sleep(Duration::from_secs(3600)).await;
         }
     });

+ 13 - 6
src/json_db_utils.rs

@@ -211,14 +211,21 @@ pub fn get_symbols_by_exchange(exchange: &str) -> Value {
 
     let path_str = format!("./db/{}", exchange);
     let path = Path::new(&path_str);
-
-    if let Some(latest_dir) = find_latest_directory(path).unwrap() {
-        info!("找到最后一日生成的目录: {}", latest_dir.to_str().unwrap());
-        let subdirectories = list_directories(&latest_dir).unwrap();
-        for sub_dir in subdirectories {
-            symbols.push(sub_dir.file_name().unwrap().to_str().unwrap().to_string())
+    let latest_directory = find_latest_directory(path);
+    match latest_directory {
+        Ok(dir) => {
+            let latest_dir = dir.unwrap();
+            info!("找到最后一日生成的目录: {}", latest_dir.to_str().unwrap());
+            let subdirectories = list_directories(&latest_dir).unwrap();
+            for sub_dir in subdirectories {
+                symbols.push(sub_dir.file_name().unwrap().to_str().unwrap().to_string())
+            }
+        }
+        Err(_) => {
+            return serde_json::to_value(&symbols).unwrap();
         }
     }
+
     return serde_json::to_value(&symbols).unwrap();
 }
 

+ 2 - 0
src/main.rs

@@ -3,6 +3,7 @@ mod control_c;
 mod server;
 mod listener_tools;
 mod gate_usdt_swap_data_listener;
+mod coinex_usdt_swap_data_listener;
 mod msv;
 mod rank;
 
@@ -30,6 +31,7 @@ async fn main() {
     control_c::exit_handler(running.clone());
     // 启动各交易所的数据监听器
     gate_usdt_swap_data_listener::run_listener(running.clone()).await;
+    coinex_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| {

+ 6 - 3
src/server.rs

@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
 use serde_json::{json, Value};
 use tracing::{info};
 use actix_cors::Cors;
-use crate::{gate_usdt_swap_data_listener, rank};
+use crate::{coinex_usdt_swap_data_listener, gate_usdt_swap_data_listener, rank};
 
 // 定义用于反序列化查询参数的结构体
 #[derive(Serialize, Deserialize, Clone)]
@@ -50,6 +50,9 @@ async fn get_rank_list(query: web::Query<RankQuery>) -> impl Responder {
         "gate_usdt_swap" => {
             gate_usdt_swap_data_listener::INDICATOR_MAP.lock().await
         },
+        "coinex_usdt_swap" => {
+            coinex_usdt_swap_data_listener::INDICATOR_MAP.lock().await
+        },
         _ => {
             let response = Response {
                 query: serde_json::to_value(&query.into_inner()).unwrap(),
@@ -89,11 +92,11 @@ async fn get_exchanges() -> impl Responder {
         // "coinsph_usdt_swap"
         // "woo_usdt_swap",
         // "cointr_usdt_swap",
-        "gate_usdt_swap",
         // "binance_usdt_swap",
-        // "coinex_usdt_swap",
         // "htx_usdt_swap",
         // "phemex_usdt_swap",
+        "gate_usdt_swap",
+        "coinex_usdt_swap",
     ];
     let response_data = json!(exchanges);
 

+ 626 - 0
standard/src/coinex_swap.rs

@@ -0,0 +1,626 @@
+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 rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use serde_json::{Value};
+use tracing::{error, info, trace};
+use exchanges::coinex_swap_rest::CoinexSwapRest;
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, PositionModeEnum};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct CoinexSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: CoinexSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl CoinexSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> CoinexSwap {
+        let market = Market::new();
+        let mut coinex_swap = CoinexSwap {
+            exchange: ExchangeEnum::CoinexSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: CoinexSwapRest::new(params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+
+        // 修改持仓模式
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mode_result = coinex_swap.set_dual_mode(symbol_array[1], true).await;
+        match mode_result {
+            Ok(_) => {
+                trace!("Coinex:设置持仓模式成功!")
+            }
+            Err(error) => {
+                error!("Coinex:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 获取市场信息
+        coinex_swap.market = CoinexSwap::get_market(&mut coinex_swap).await.unwrap_or(coinex_swap.market);
+        // 设置持仓杠杆
+        let lever_rate_result = coinex_swap.set_dual_leverage("10").await;
+        match lever_rate_result {
+            Ok(ok) => {
+                info!("Coinex:设置持仓杠杆成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("Coinex:设置持仓杠杆失败!{:?}", error)
+            }
+        }
+        return coinex_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for CoinexSwap {
+    // 克隆方法
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+    // 获取交易所模式
+    fn get_self_exchange(&self) -> ExchangeEnum {
+        ExchangeEnum::CoinexSwap
+    }
+    // 获取交易对
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+    // 获取是否使用高速通道
+    fn get_self_is_colo(&self) -> bool {
+        self.is_colo
+    }
+    // 获取params信息
+    fn get_self_params(&self) -> BTreeMap<String, String> {
+        self.params.clone()
+    }
+    // 获取market信息
+    fn get_self_market(&self) -> Market { self.market.clone() }
+    // 获取请求时间
+    fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+    // 获取请求平均时间
+    fn get_request_avg_delay(&self) -> Decimal { self.request.get_avg_delay() }
+    // 获取请求最大时间
+    fn get_request_max_delay(&self) -> i64 { self.request.get_max_delay() }
+
+    // 获取服务器时间
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let res_data = self.request.get_server_time().await;
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data;
+            let result = res_data_json["timestamp"].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 coin = symbol_array[1].to_string().to_uppercase();
+        let res_data = self.request.get_account().await;
+        if res_data.code == 200 {
+            let res_data_array = res_data.data.as_array().unwrap();
+            for res_data_json in res_data_array.iter() {
+                if res_data_json["ccy"].as_str().unwrap() == coin {
+                    let frozen_balance= Decimal::from_str(res_data_json["frozen"].as_str().unwrap()).unwrap();
+                    let available_balance = Decimal::from_str(res_data_json["available"].as_str().unwrap()).unwrap();
+                    let balance = frozen_balance + available_balance;
+                    let result = Account {
+                        coin: symbol_array[1].to_string(),
+                        balance,
+                        available_balance,
+                        frozen_balance,
+                        stocks: Decimal::ZERO,
+                        available_stocks: Decimal::ZERO,
+                        frozen_stocks: Decimal::ZERO,
+                    };
+                    return Ok(result);
+                }
+            }
+        }
+        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, "coinex_swap:该方法暂未实现".to_string()))
+    }
+
+    // 获取持仓信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        let symbol: String = self.symbol.replace("_", "").to_uppercase();
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.get_position(symbol).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    // 获取所有持仓
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        let res_data = self.request.get_user_position().await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let result = res_data_json.iter().map(|item| { format_position_item(item, Decimal::ONE) }).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+    // 获取市场行情
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        let symbol: String = self.symbol.replace("_", "");
+        let res_data = self.request.get_ticker(symbol.clone()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let ticker_info = res_data_json.iter().find(|item| item["market"].as_str().unwrap() == symbol);
+            match ticker_info {
+                None => {
+                    error!("coinex_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: chrono::Utc::now().timestamp_millis(),
+                        high: Decimal::from_str(value["high"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["volume"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol_param: String) -> Result<Ticker, Error> {
+        let symbol: String = symbol_param.replace("_", "").to_uppercase();
+        let res_data = self.request.get_ticker(symbol.clone()).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == symbol);
+            match ticker_info {
+                None => {
+                    error!("coinex_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let result = Ticker {
+                        time: chrono::Utc::now().timestamp_millis(),
+                        high: Decimal::from_str(value["high"].as_str().unwrap()).unwrap(),
+                        low: Decimal::from_str(value["low"].as_str().unwrap()).unwrap(),
+                        sell: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        buy: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
+                        volume: Decimal::from_str(value["volume"].as_str().unwrap()).unwrap(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let symbol = format!("{}{}", symbol_array[0], symbol_array[1]);
+        let res_data = self.request.get_market_details(symbol.clone()).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["market"].as_str().unwrap() == symbol);
+            match market_info {
+                None => {
+                    error!("coinex_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    // 报价精度字符串
+                    let price_precision_i64 = value["quote_ccy_precision"].as_i64().unwrap();
+                    // 价格最小变动数值
+                    let tick_size = Decimal::new(1, 0) / Decimal::new(10i64.pow(price_precision_i64.to_u32().unwrap()), 0);
+                    // 报价精度
+                    let price_precision = Decimal::from_i64(price_precision_i64).unwrap();
+                    // 最小数量
+                    let min_qty = Decimal::from_str(value["min_amount"].as_str().unwrap()).unwrap();
+                    // 数量没有最大值
+                    let max_qty = Decimal::MAX;
+                    // 没有张数
+                    let ct_val = Decimal::ONE;
+
+                    let amount_size = min_qty * ct_val;
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+                    let max_notional = max_qty * ct_val;
+
+                    let result = Market {
+                        symbol: self.symbol.clone(),
+                        base_asset: symbol_array[0].to_string(),
+                        quote_asset: symbol_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_upper = symbol.to_uppercase();
+        let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
+        let symbol = format!("{}{}", symbol_array[0], symbol_array[1]);
+        let res_data = self.request.get_market_details(symbol.clone()).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["name"].as_str().unwrap() == symbol.clone());
+            match market_info {
+                None => {
+                    error!("coinex_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let tick_size = Decimal::from_str(value["quote_ccy_precision"].as_str().unwrap()).unwrap();
+                    let min_qty = Decimal::from_str(&value["min_amount"].to_string()).unwrap();
+                    // 数量没有最大值
+                    let max_qty = Decimal::MAX;
+                    // 没有张数
+                    let ct_val = Decimal::ONE;
+
+                    let amount_size = min_qty * ct_val;
+                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_notional = min_qty * ct_val;
+                    let max_notional = max_qty * ct_val;
+
+                    let result = Market {
+                        symbol: self.symbol.clone(),
+                        base_asset: symbol_array[0].to_string(),
+                        quote_asset: symbol_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    // 获取订单详情
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol = self.symbol.replace("_", "").to_uppercase();
+        let ct_val = self.market.ct_val;
+        let res_data;
+        if order_id != "" {
+            res_data = self.request.get_order_details(order_id.to_string(), symbol).await;
+        } else if custom_id != "" {
+            // 通过客户端id查询  只有未完成的订单才能查询出来
+            res_data = self.request.get_pending_order(format!("t-{}", custom_id)).await;
+        } else {
+            return Err(Error::new(ErrorKind::Other, format!("订单id和客户端id都为空,查询失败!order_id :{}, custom_id: {}", order_id, custom_id)));
+        }
+
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data;
+            let mut result = format_order_item(res_data_json, ct_val, "");
+            result.custom_id = custom_id.to_string();
+            result.id = order_id.to_string();
+            Ok(result)
+        } else if res_data.code == -1 && res_data.message.contains("3103:order not exists") { // 未成交已取消的订单会报不存在
+            let mut order = Order::new();
+            order.id = order_id.to_string();
+            order.custom_id = custom_id.to_string();
+            order.status = "REMOVE".to_string();
+            Ok(order)
+        } 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 = self.symbol.replace("_", "").to_uppercase();
+        let ct_val = self.market.ct_val;
+        let status_order;
+        let res_data;
+        if status == "pending" {
+            res_data = self.request.get_pending_orders().await;
+            status_order = "open";
+        } else if status == "finish" {
+            res_data = self.request.get_finished_orders().await;
+            status_order = "filled";
+        }else{
+            return Err(Error::new(ErrorKind::Other, status));
+        }
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let order_info: Vec<_> = res_data_json.iter().filter(|item| item["market"].as_str().unwrap_or("") == symbol).collect();
+            let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val, status_order)).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 = self.symbol.replace("_", "").to_uppercase();
+        let ct_val = self.market.ct_val;
+        let order_side;
+        let position_side;
+        let size = (amount / ct_val).floor();
+        match origin_side {
+            "kd" => {
+                position_side = "long";
+                order_side = "buy";
+            }
+            "pd" => {
+                position_side = "long";
+                order_side = "sell";
+            }
+            "kk" => {
+                position_side = "short";
+                order_side = "sell";
+            }
+            "pk" => {
+                position_side = "short";
+                order_side = "buy";
+            }
+            _ => {
+                error!("下单参数错误");
+                position_side = "error";
+                order_side = "error";
+            }
+        };
+        let res_data = self.request.order(symbol, position_side.to_string(), order_side.to_string(), size, price, custom_id.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data;
+            // info!("take_order {}", res_data_json);
+            let result = format_order_item(res_data_json, ct_val, "open");
+            Ok(result)
+        } else {
+            // error!("take_order error {}", res_data.data);
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_order_symbol(&mut self, symbol_y: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol = symbol_y.replace("_", "").to_uppercase();
+        let order_side;
+        let position_side;
+        let size = (amount / ct_val).floor();
+        match origin_side {
+            "kd" => {
+                position_side = "long";
+                order_side = "buy";
+            }
+            "pd" => {
+                position_side = "long";
+                order_side = "sell";
+            }
+            "kk" => {
+                position_side = "short";
+                order_side = "sell";
+            }
+            "pk" => {
+                position_side = "short";
+                order_side = "buy";
+            }
+            _ => {
+                error!("下单参数错误");
+                position_side = "error";
+                order_side = "error";
+            }
+        };
+        let res_data = self.request.order(symbol, position_side.to_string(), order_side.to_string(), size, price, custom_id.to_string()).await;
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data;
+            // info!("take_order_symbol {}", res_data_json);
+            let result = format_order_item(res_data_json, ct_val, "open");
+            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 = self.symbol.replace("_", "").to_uppercase();
+
+        let ct_val = self.market.ct_val;
+        let res_data = self.request.cancel_order(symbol, order_id, custom_id).await;
+        if res_data.code == 200 {
+            let res_data_json: Value = res_data.data;
+            // info!("cancel_order {} order_id {} custom_id {}", res_data_json, order_id, custom_id);
+            let mut result = format_order_item(res_data_json, ct_val, "canceled");
+            result.custom_id = custom_id.to_string();
+            result.id = order_id.to_string();
+            Ok(result)
+        } else {
+            let message = format!("撤单HTTP请求失败  order_id: {}, custom_id: {}, res_data: {:?}", order_id, custom_id, res_data);
+            Err(Error::new(ErrorKind::Other, message))
+        }
+    }
+    // 批量撤销订单
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol = self.symbol.replace("_", "").to_uppercase();
+        let res_data = self.request.cancel_order_all(symbol).await;
+        if res_data.code == 200 {
+            // let res_data_json = res_data.data.as_array().unwrap();
+            // info!("cancel_orders {:?}", res_data_json);
+            let result = vec![];
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        let ct_val = self.market.ct_val;
+        let orders_res_data = self.request.get_pending_orders().await;
+        let status = "canceled";
+        if orders_res_data.code == 200 {
+            let mut result = vec![];
+            let orders_res_data_json = orders_res_data.data.as_array().unwrap();
+            for order in orders_res_data_json {
+                let cancel_res_data = self.request.cancel_order_all( order["market"].as_str().unwrap().to_string()).await;
+                // info!("cancel_orders_all {:?}", cancel_res_data);
+                if cancel_res_data.code == 200 {
+                    result.push(format_order_item(order.clone(), ct_val, status))
+                } else {
+                    return Err(Error::new(ErrorKind::Other, cancel_res_data.to_string()));
+                }
+            }
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, orders_res_data.to_string()))
+        }
+    }
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所暂未实现自动订单下单".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所暂未实现取消自动订单".to_string()))
+    }
+
+    // 设置持仓模式
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所只允许单向持仓,无法设置持仓模式".to_string()))
+    }
+
+    // 更新杠杆
+    async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
+        let leverage_int = leverage.parse::<i32>().unwrap();
+        let symbol = self.symbol.replace("_", "").to_uppercase();
+        let res_data = self.request.setting_dual_leverage(symbol, leverage_int).await;
+        if res_data.code == 200 {
+            let res_data_str = &res_data.data;
+            let result = res_data_str.clone();
+            Ok(result.to_string())
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "Coinex:该交易所方法未实现".to_string())) }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "Coinex wallet_transfers:该交易所方法未实现".to_string()))
+    }
+}
+
+pub fn format_position_item(position: &Value, ct_val: Decimal) -> Position {
+    let position_mode = match position["side"].as_str().unwrap_or("") {
+        "long" => PositionModeEnum::Long,
+        "short" => PositionModeEnum::Short,
+        _ => {
+            error!("coinex_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("coinex_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size = Decimal::from_str(&position["open_interest"].as_str().unwrap()).unwrap();
+    let amount = size * ct_val;
+    Position {
+        symbol: position["market"].as_str().unwrap_or("").parse().unwrap(),
+        margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(position["avg_entry_price"].as_str().unwrap()).unwrap(),
+        profit: Decimal::from_str(position["unrealized_pnl"].as_str().unwrap()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(position["ath_margin_size"].as_str().unwrap()).unwrap(),
+    }
+}
+
+pub fn format_order_item(order: Value, ct_val: Decimal, status :&str) -> Order {
+    if order.is_null() || (order.is_array() && order.as_array().unwrap().is_empty()){
+        return Order {
+            id: "".to_string(),
+            custom_id: "".to_string(),
+            price: Decimal::ZERO,
+            amount: Decimal::ZERO,
+            deal_amount: Decimal::ZERO,
+            avg_price: Decimal::ZERO,
+            status: "REMOVE".to_string(),
+            order_type: "limit".to_string(),
+        }
+    }
+    let size;
+    match order["amount"].as_str() {
+        Some(val) => {
+            size = Decimal::from_str(val).unwrap()
+        },
+        None => {
+            error!("coinex_swap:格式化订单大小错误!\nformat_order_item:order={:?} status={}", order, status);
+            panic!("coinex_swap:格式化订单大小失败,退出程序!");
+        }
+    }
+    // info!("order {}", order);
+    // 通过客户端id查询订单 只能查出未完成订单(没有状态字段)
+    let status = order["status"].as_str().unwrap_or(status);
+    let text = order["client_id"].as_str().unwrap_or("");
+
+    let deal_amount = Decimal::from_str(&order["filled_amount"].as_str().unwrap()).unwrap();
+    let filled_value = Decimal::from_str(&order["filled_value"].as_str().unwrap()).unwrap();
+
+    let amount = size * ct_val;
+    let mut avg_price = Decimal::ZERO;
+    if deal_amount != Decimal::ZERO{
+        avg_price = filled_value/deal_amount;
+    }
+    let custom_status = if status == "filled" || status == "canceled" {
+        "REMOVE".to_string()
+    } else if status == "open" || status == "part_filled" || status == "part_canceled" {
+        "NEW".to_string()
+    } else {
+        error!("coinex_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order);
+        "NULL".to_string()
+    };
+    let rst_order = Order {
+        id: order["order_id"].to_string(),
+        custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
+        price: Decimal::from_str(order["price"].as_str().unwrap()).unwrap(),
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: "limit".to_string(),
+    };
+    return rst_order;
+}

+ 33 - 0
standard/src/coinex_swap_handle.rs

@@ -0,0 +1,33 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use exchanges::response_base::ResponseData;
+use crate::{Trade};
+
+pub fn format_trade_items(response: &ResponseData) -> Vec<Trade> {
+    let symbol = response.data["market"].as_str().unwrap().to_string().replace("USDT", "_USDT");
+    let result = response.data["deal_list"].as_array().unwrap();
+    let mut trades = vec![];
+
+    for item in result {
+        let id = format!("{}", item["deal_id"].as_i64().unwrap());
+        let time = Decimal::from_i64(item["created_at"].as_i64().unwrap()).unwrap();
+        let mut size = Decimal::from_str(item["amount"].as_str().unwrap()).unwrap();
+        if item["side"].as_str().unwrap().eq("sell") {
+            size = size * Decimal::NEGATIVE_ONE;
+        }
+        let price = Decimal::from_str(item["price"].as_str().unwrap().to_string().as_str()).unwrap();
+
+        let trade = Trade {
+            id,
+            time,
+            size,
+            price,
+            symbol: symbol.clone(),
+        };
+
+        trades.push(trade)
+    }
+
+    return trades;
+}

+ 5 - 0
standard/src/exchange.rs

@@ -2,6 +2,7 @@ use std::collections::{BTreeMap};
 use std::io::Error;
 use tokio::sync::mpsc::Sender;
 use crate::{Order, Platform};
+use crate::coinex_swap::CoinexSwap;
 use crate::gate_swap::GateSwap;
 
 
@@ -14,6 +15,7 @@ use crate::gate_swap::GateSwap;
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ExchangeEnum {
     GateSwap,
+    CoinexSwap,
 }
 
 /// Exchange结构体
@@ -61,6 +63,9 @@ impl Exchange {
             ExchangeEnum::GateSwap => {
                 Box::new(GateSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
+            ExchangeEnum::CoinexSwap => {
+                Box::new(CoinexSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
         }
     }
 }

+ 4 - 1
standard/src/exchange_struct_handler.rs

@@ -1,6 +1,6 @@
 use exchanges::response_base::ResponseData;
 use crate::exchange::ExchangeEnum;
-use crate::{gate_swap_handle};
+use crate::{coinex_swap_handle, gate_swap_handle};
 use crate::{ Trade };
 
 #[allow(dead_code)]
@@ -14,6 +14,9 @@ impl ExchangeStructHandler {
             ExchangeEnum::GateSwap => {
                 gate_swap_handle::format_trade_items(&res_data)
             }
+            ExchangeEnum::CoinexSwap => {
+                coinex_swap_handle::format_trade_items(&res_data)
+            }
         }
     }
 }

+ 2 - 0
standard/src/lib.rs

@@ -15,6 +15,8 @@ pub mod exchange_struct_handler;
 // 引入gate模块
 mod gate_swap;
 pub mod gate_swap_handle;
+pub mod coinex_swap;
+pub mod coinex_swap_handle;
 
 /// 持仓模式枚举
 /// - `Both`:单持仓方向