Преглед изворни кода

初始化火币交易所的文件,解决coinexhttp请求被重置问题

JiahengHe пре 1 година
родитељ
комит
0c45aa605d

+ 15 - 8
exchanges/src/coinex_swap_rest.rs

@@ -546,14 +546,21 @@ impl CoinexSwapRest {
         };
 
         // 读取响应的内容
-        let response = request_builder.send().await.unwrap();
-        let is_success = response.status().is_success(); // 先检查状态码
-        let text = response.text().await.unwrap();
-        let data_json: Value = serde_json::from_str(text.as_str()).unwrap();
-        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)
+        let res = request_builder.send().await;
+        match res {
+            Ok(response) => {
+                let is_success = response.status().is_success(); // 先检查状态码
+                let text = response.text().await.unwrap();
+                let data_json: Value = serde_json::from_str(text.as_str()).unwrap();
+                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) => {// 异常情况
+                self.on_error_data(&e.to_string(), &url, &body)
+            }
         }
     }
 

+ 613 - 0
exchanges/src/huobi_swap_rest.rs

@@ -0,0 +1,613 @@
+use std::collections::BTreeMap;
+use reqwest::header::HeaderMap;
+use ring::{digest};
+use hex;
+use hmac::{Hmac, Mac, NewMac};
+use reqwest::Client;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::Value;
+use crate::http_tool::RestTool;
+use crate::response_base::ResponseData;
+use sha2::Sha512;
+use tracing::{error, info};
+
+#[derive(Clone)]
+pub struct HuobiSwapRest {
+    label: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //登录所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl HuobiSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> HuobiSwapRest
+    {
+        return HuobiSwapRest::new_label("default-HuobiSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> HuobiSwapRest
+    {
+        let base_url = if is_colo {
+            info!("不支持colo高速线路");
+            let url = "https://api.hbdm.com".to_string();
+            url
+        } else {
+            let url = "https://api.hbdm.com".to_string();
+            url
+        };
+
+
+        if is_colo {} else {}
+        /*****返回结构体*******/
+        HuobiSwapRest {
+            label,
+            base_url: base_url.to_string(),
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //获取服务器当前时间
+    pub async fn get_server_time(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/spot/time"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询个人交易费率
+    pub async fn wallet_fee(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/wallet/fee"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询合约账户
+    pub async fn get_account(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/accounts", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //用户仓位列表
+    pub async fn get_user_position(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/positions", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //双仓模式下的持仓信息
+    pub async fn get_position(&mut self, settle: String, contract: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/dual_comp/positions/{}", settle, contract),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //获取所有合约交易行情统计
+    pub async fn get_ticker(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/tickers", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询所有的合约信息
+    pub async fn get_market_details(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/contracts", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询单个订单详情
+    pub async fn get_order_details(&mut self, settle: String, order_id: String) -> ResponseData {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders/{}", settle, order_id),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查询合约订单列表
+    pub async fn get_orders(&mut self, settle: String, status: String) -> ResponseData {
+        let params = serde_json::json!({
+            "status":status
+         });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //下单:side-下单方向,pos_side-持仓方向
+    pub async fn order(&mut self,
+                       settle: String,
+                       pos_side: String,
+                       side: String,
+                       contract: String,
+                       size: i64,
+                       price: String,
+                       text: String,
+    ) -> ResponseData
+    {
+        if side != "buy" && side != "sell" {
+            ResponseData::error(self.label.clone(), format!("未知下单方向!{}", side));
+        }
+        if pos_side != "long" && pos_side != "short" {
+            ResponseData::error(self.label.clone(), format!("未知持仓方向!{}", side));
+        }
+        let mut param = serde_json::json!({
+            "contract":contract, //合约标识
+            "size":size,
+            "price":price,
+            "text":text,
+        });
+        if price == "0" {
+            param.as_object_mut().unwrap().insert("tif".to_string(), serde_json::json!("ioc"));
+        }
+        if size == 0 {   //数量为0则平仓
+            param.as_object_mut().unwrap().insert("close".to_string(), serde_json::json!(true));
+        }
+        match format!("{}_{}", pos_side, side).as_str() {
+            "long_buy" => {//开多
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(false));
+            }
+            "long_sell" => {//平多
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(true));
+            }
+            "short_buy" => {//平空
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(true));
+            }
+            "short_sell" => {//开空
+                param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(false));
+            }
+            _ => {} // 处理未知请求类型
+        };
+        // trace!("----param{}", param.to_string());
+        let data = self.swap_order(settle, param).await;
+        data
+    }
+    //合约交易下单
+    pub async fn swap_order(&mut self, settle: String, params: serde_json::Value) -> ResponseData {
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    // 提交一个自动订单
+    pub async fn place_price_order(&mut self, settle: String, params: Value) -> ResponseData {
+        self.request("POST".to_string(),
+                     "/api/v4".to_string(),
+                     format!("/futures/{}/price_orders", settle),
+                     true,
+                     params.to_string()).await
+    }
+    // 撤销自动订单
+    pub async fn cancel_price_order(&mut self, settle: String, order_id: String) -> ResponseData {
+        self.request("DELETE".to_string(),
+                     "/api/v4".to_string(),
+                     format!("/futures/{}/price_orders/{}", settle, order_id),
+                     true,
+                     "{}".to_string(),
+        ).await
+    }
+    //设置持仓模式
+    pub async fn setting_dual_mode(&mut self, settle: String, dual_mode: bool) -> ResponseData {
+        let params = serde_json::json!({
+                "dual_mode":dual_mode,
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/dual_mode", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //更新双仓模式下的杠杆
+    pub async fn setting_dual_leverage(&mut self, settle: String, symbol: String, leverage: String) -> ResponseData {
+        let params = serde_json::json!({
+                "leverage":leverage,
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/dual_comp/positions/{}/leverage", settle, symbol),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //交易账户互转
+    pub async fn wallet_transfers(&mut self, currency: String, from: String, to: String, amount: String, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+                "currency":currency,
+                "from":from,
+                "to":to,
+                "amount":amount,
+                "settle":settle,
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/wallet/transfers"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //撤销单个订单
+    pub async fn cancel_order(&mut self, settle: String, order_id: String) -> ResponseData {
+        let params = serde_json::json!({
+             });
+
+        let data = self.request("DELETE".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders/{}", settle, order_id),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //撤销所有挂单
+    pub async fn cancel_order_all(&mut self) -> ResponseData {
+        let params = serde_json::json!({
+             });
+        let data = self.request("POST".to_string(),
+                                "/api/v5".to_string(),
+                                format!("/sprd/mass-cancel"),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //弃用
+    pub async fn swap_bazaar_order(&mut self, text: String, origin_side: String, settle: String, contract: String, size: i64) -> ResponseData {
+        let mut reduce_only = false;
+        let mut param = serde_json::json!({
+            "text":text,
+            "contract":contract,
+            "price":"0",
+            "size":size,
+        });
+
+        let req = match origin_side.as_str() {
+            "kd" => {
+                reduce_only = false;
+                true
+            }
+            "pd" => {
+                reduce_only = true;
+                true
+            }
+            "kk" => {
+                reduce_only = false;
+                true
+            }
+            "pk" => {
+                reduce_only = true;
+                true
+            }
+            _ => { false } // 处理未知请求类型
+        };
+        if req {
+            param.as_object_mut().unwrap().insert("reduce_only".to_string(), serde_json::json!(reduce_only));
+        }
+
+        let data = self.swap_order(settle, param).await;
+        data
+    }
+
+    //批量取消状态为 open 的订单
+    pub async fn cancel_orders(&mut self, settle: String, contract: String) -> ResponseData {
+        let params = serde_json::json!({
+            "contract":contract
+             });
+
+        let data = self.request("DELETE".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/orders", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查询个人成交记录
+    pub async fn my_trades(&mut self, settle: String, contract: String, limit: i64) -> ResponseData {
+        let mut params = serde_json::json!({
+            "contract":contract,
+            "limit":1000
+        });
+        if limit > 0 {
+            params["limit"] = serde_json::json!(limit);
+        }
+
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/my_trades", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查询合约账户变更历史
+    pub async fn account_book(&mut self, settle: String) -> ResponseData {
+        let params = serde_json::json!({
+                "limit":200
+             });
+        let data = self.request("GET".to_string(),
+                                "/api/v4".to_string(),
+                                format!("/futures/{}/account_book", settle),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    pub fn get_delays(&self) -> Vec<i64> {
+        self.delays.clone()
+    }
+    pub fn get_avg_delay(&self) -> Decimal {
+        self.avg_delay.clone()
+    }
+    pub fn get_max_delay(&self) -> i64 {
+        self.max_delay.clone()
+    }
+    fn get_delay_info(&mut self) {
+        let last_100 = if self.delays.len() > 100 {
+            self.delays[self.delays.len() - 100..].to_vec()
+        } else {
+            self.delays.clone()
+        };
+
+        let max_value = last_100.iter().max().unwrap();
+        if max_value.clone().to_owned() > self.max_delay {
+            self.max_delay = max_value.clone().to_owned();
+        }
+
+        let sum: i64 = last_100.iter().sum();
+        let sum_v = Decimal::from_i64(sum).unwrap();
+        let len_v = Decimal::from_u64(last_100.len() as u64).unwrap();
+        self.avg_delay = (sum_v / len_v).round_dp(1);
+        self.delays = last_100.clone().into_iter().collect();
+    }
+
+    //调用请求
+    async fn request(&mut self,
+                     requesst_type: String,
+                     prefix_url: String,
+                     request_url: String,
+                     is_login: bool,
+                     params: String) -> ResponseData
+    {
+        // trace!("login_param:{:?}", self.login_param);
+        //解析账号信息
+        let mut access_key = "".to_string();
+        let mut secret_key = "".to_string();
+        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 mut body = "".to_string();
+        let timestamp = chrono::Utc::now().timestamp().to_string();
+
+        let mut headers = HeaderMap::new();
+        if requesst_type == "GET" {
+            headers.insert("Content-type", "application/x-www-form-urlencoded".parse().unwrap());
+            headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ".parse().unwrap());
+        } else {
+            headers.insert("Accept", "application/json".parse().unwrap());
+            headers.insert("Content-Type", "application/json".parse().unwrap());
+        }
+
+        if requesst_type == "POST" {
+            body = params.clone();
+        }
+
+        //是否需要登录-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let e = ResponseData::error(self.label.clone(), "登录参数错误!".to_string());
+                return e;
+            } else {//需要登录-且登录参数齐全
+                //组装sing
+                let sing = Self::sign(secret_key.clone(),
+                                      requesst_type.clone(),
+                                      prefix_url.clone(),
+                                      request_url.clone(),
+                                      params.clone(),
+                                      body.clone(),
+                                      timestamp.clone(),
+                );
+                // trace!("sing:{}", sing);
+                //组装header
+                headers.extend(Self::headers(access_key, timestamp, sing));
+            }
+        }
+
+        // trace!("headers:{:?}", headers);
+        let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let response = self.http_tool(
+            base_url.clone(),
+            requesst_type.to_string(),
+            params.clone(),
+            headers,
+        ).await;
+
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+
+        response
+    }
+
+    pub fn headers(access_key: String, timestamp: String, sign: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("KEY", access_key.clone().parse().unwrap());
+        headers.insert("Timestamp", timestamp.clone().parse().unwrap());
+        headers.insert("SIGN", sign.clone().parse().unwrap());
+        headers
+    }
+    pub fn sign(secret_key: String,
+                requesst_type: String, prefix_url: String, request_url: String,
+                params: String, body_data: String, timestamp: String) -> String
+    {
+        let url = format!("{}{}", prefix_url, request_url);
+        let params_str = RestTool::parse_params_to_str(params);
+        let body = Some(body_data);
+        let hashed_payload = if let Some(body) = body {
+            let mut m = digest::Context::new(&digest::SHA512);
+            m.update(body.as_bytes());
+            hex::encode(m.finish().as_ref())
+        } else {
+            String::new()
+        };
+        // trace!("hashed_payload:{}", hashed_payload);
+
+        let message = format!("{}\n{}\n{}\n{}\n{}",
+                              requesst_type,
+                              url,
+                              params_str,
+                              hashed_payload,
+                              timestamp);
+        // trace!("**********", );
+        // trace!("组装数据:{}", message);
+        // trace!("**********", );
+
+        let mut mac = Hmac::<Sha512>::new_varkey(secret_key.as_bytes()).expect("Failed to create HMAC");
+        mac.update(message.as_bytes());
+        let result = mac.finalize().into_bytes();
+        let sign = hex::encode(result);
+        sign
+    }
+
+
+    async fn http_tool(&mut self, request_path: String, request_type: String, params: String, headers: HeaderMap) -> ResponseData {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let addrs_url = format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()));
+
+        let request_builder = match request_type.as_str() {
+            "GET" => self.client.get(addrs_url.clone()).headers(headers),
+            "POST" => self.client.post(addrs_url.clone()).body(params.clone()).headers(headers),
+            "DELETE" => self.client.delete(addrs_url.clone()).headers(headers),
+            // "PUT" => self.client.put(url.clone()).json(&params),
+            _ => {
+                panic!("{}", format!("错误的请求类型:{}", request_type.clone()))
+            }
+        };
+
+        // 读取响应的内容
+        let response = request_builder.send().await.unwrap();
+        let is_success = response.status().is_success(); // 先检查状态码
+        let text = response.text().await.unwrap();
+        return if is_success {
+            self.on_success_data(&text)
+        } else {
+            self.on_error_data(&text, &addrs_url, &params)
+        }
+    }
+
+    pub fn on_success_data(&mut self, text: &String) -> ResponseData {
+        let data = serde_json::from_str(text.as_str()).unwrap();
+
+        ResponseData::new(self.label.clone(), 200, "success".to_string(), data)
+    }
+
+    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["label"].as_str().unwrap(), data["message"].as_str().unwrap());
+                } else {
+                    message = data["label"].to_string();
+                }
+
+                let mut error = ResponseData::error(self.label.clone(), message);
+                error.message = format!("请求地址:{}, 请求参数:{}, 报错内容:{}。", base_url, params, error.message);
+                error
+            }
+            Err(e) => {
+                error!("解析错误:{:?}", e);
+                let error = ResponseData::error("".to_string(),
+                                                format!("json 解析失败:{},相关参数:{}", e, text));
+                error
+            }
+        }
+    }
+}

+ 0 - 0
exchanges/src/huobi_swap_ws.rs


+ 2 - 0
exchanges/src/lib.rs

@@ -27,4 +27,6 @@ pub mod xlsx_utils;
 pub mod bybit_swap_ws;
 pub mod coinex_swap_rest;
 pub mod coinex_swap_ws;
+mod huobi_swap_rest;
+mod huobi_swap_ws;