hl преди 1 година
родител
ревизия
249abb6076
променени са 6 файла, в които са добавени 809 реда и са изтрити 105 реда
  1. 199 103
      src/export_balance.rs
  2. 3 2
      src/main.rs
  3. 26 0
      src/swap_bybit/bybit_swap_rest.rs
  4. 395 0
      src/swap_bybit/bybit_swap_rest_utils.rs
  5. 183 0
      src/swap_bybit/bybit_swap_standard.rs
  6. 3 0
      src/swap_bybit/mod.rs

+ 199 - 103
src/export_balance.rs

@@ -1,4 +1,3 @@
-use std::cmp::min;
 use std::collections::{BTreeMap};
 use std::str::FromStr;
 use chrono::NaiveDateTime;
@@ -6,6 +5,7 @@ use rust_decimal::Decimal;
 use rust_decimal_macros::dec;
 use tracing::{error, info};
 use crate::{export_template};
+use crate::swap_bybit::bybit_swap_rest_utils::BybitSwapRest;
 use crate::swap_gate::gate_swap_rest_utils::GateSwapRest;
 use crate::utils::utils::BalanceConfigInfo;
 
@@ -19,97 +19,195 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
     //获取不同账号的数据
     let mut all_account_balance_info: BTreeMap<String, Vec<Vec<String>>> = BTreeMap::new();
     let mut account_name_list: Vec<String> = vec![];
-    loop {
-        if config_info_clone.account_list.len() == 0 {
-            info!("没有账号信息");
-            return;
-        }
+    if config_info_clone.account_list.len() == 0 {
+        info!("没有账号信息");
+        return;
+    }
+    for exchange in config_info.exchanges.clone() {
+        let exchange_up = exchange.to_uppercase();
+        let exchange_result = match exchange_up.as_str() {
+            "GATE" => {
+                for account in config_info_clone.account_list.clone() {
+                    let mut account_info: BTreeMap<String, String> = BTreeMap::new();
+                    account_info.insert("account_name".to_string(), account[0].clone());
+                    account_info.insert("access_key".to_string(), account[1].clone());
+                    account_info.insert("secret_key".to_string(), account[2].clone());
 
-        for account in config_info_clone.account_list.clone() {
-            let mut account_info: BTreeMap<String, String> = BTreeMap::new();
-            account_info.insert("account_name".to_string(), account[0].clone());
-            account_info.insert("access_key".to_string(), account[1].clone());
-            account_info.insert("secret_key".to_string(), account[2].clone());
-
-            let mut gate_exc = GateSwapRest::new(false, account_info.clone());
-            let data = gate_exc.account_book("usdt".to_string()).await;
-            // info!("请求完成{:?}",data.clone());
-            if data.code.as_str() == "200" {
-                let account_name = account_info.get("account_name").unwrap();
-                account_name_list.push(account_name.clone());
-
-                //账号
-                let json_value: serde_json::Value = serde_json::from_str(&data.data).unwrap();
-                if let serde_json::Value::Array(array) = json_value {
-                    let mut name_data_all: Vec<Vec<String>> = vec![];
-                    let mut times = 0;
-
-                    for item in array {
-                        let time = item["time"].as_i64().unwrap();//秒级
-                        if time == times { continue; }
-                        times = time;
-
-                        let change = item["change"].as_str().unwrap();
-
-                        let balance = item["balance"].as_str().unwrap();
-                        let type_str = match item["type"].as_str().unwrap() {
-                            "dnw" => { "转入转出" }
-                            "pnl" => { "减仓盈亏" }
-                            "fee" => {
-                                "交易手续费";
-                                continue;
-                            }
-                            "refr" => {
-                                "推荐人返佣";
-                                continue;
-                            }
-                            "fund" => {
-                                "资金费用";
-                                continue;
-                            }
-                            "point_dnw" => {
-                                "点卡转入转出";
-                                continue;
-                            }
-                            "point_fee" => {
-                                "点卡交易手续费";
-                                continue;
-                            }
-                            "point_refr" => {
-                                "点卡推荐人返佣";
-                                continue;
-                            }
-                            _ => {
-                                "未知-变更类型";
-                                continue;
+                    let mut gate_exc = GateSwapRest::new(false, account_info.clone());
+                    let data = gate_exc.account_book("usdt".to_string()).await;
+                    // info!("请求完成{:?}",data.clone());
+                    if data.code.as_str() == "200" {
+                        let account_name = account_info.get("account_name").unwrap();
+                        account_name_list.push(account_name.clone());
+
+                        //账号
+                        let json_value: serde_json::Value = serde_json::from_str(&data.data).unwrap();
+                        if let serde_json::Value::Array(array) = json_value {
+                            let mut name_data_all: Vec<Vec<String>> = vec![];
+                            let mut times = 0;
+
+                            for item in array {
+                                // info!("数据{:?}",item.clone().to_string());
+
+                                let time = item["time"].as_i64().unwrap();//秒级
+                                if time == times { continue; }
+                                times = time;
+
+                                let change = item["change"].as_str().unwrap();
+
+                                let balance = item["balance"].as_str().unwrap();
+                                let type_str = match item["type"].as_str().unwrap() {
+                                    "dnw" => { "转入转出" }
+                                    "pnl" => { "减仓盈亏" }
+                                    "fee" => {
+                                        "交易手续费";
+                                        continue;
+                                    }
+                                    "refr" => {
+                                        "推荐人返佣";
+                                        continue;
+                                    }
+                                    "fund" => {
+                                        "资金费用";
+                                        continue;
+                                    }
+                                    "point_dnw" => {
+                                        "点卡转入转出";
+                                        continue;
+                                    }
+                                    "point_fee" => {
+                                        "点卡交易手续费";
+                                        continue;
+                                    }
+                                    "point_refr" => {
+                                        "点卡推荐人返佣";
+                                        continue;
+                                    }
+                                    _ => {
+                                        "未知-变更类型";
+                                        continue;
+                                    }
+                                };
+
+
+                                let text = item["text"].as_str().unwrap();
+                                let contract = item["contract"].as_str().unwrap();
+                                let trade_id = item["trade_id"].as_str().unwrap();
+
+                                let mut name_data_array: Vec<String> = vec![];
+                                name_data_array.push(time.to_string());
+                                name_data_array.push(trade_id.to_string());
+                                name_data_array.push(change.to_string());
+                                name_data_array.push(balance.to_string());
+                                name_data_array.push(type_str.to_string());
+                                name_data_array.push(contract.to_string());
+                                name_data_array.push(text.to_string());
+
+                                name_data_all.push(name_data_array.clone());
                             }
-                        };
+                            all_account_balance_info.insert(account_name.clone(), name_data_all.clone());
+                        } else {
+                            info!("不是数组 检查数据");
+                        }
+                    }
+                    // break;
+                }
+            }
+            "BYBIT" => {
+                for account in config_info_clone.account_list.clone() {
+                    let mut account_info: BTreeMap<String, String> = BTreeMap::new();
+                    account_info.insert("account_name".to_string(), account[0].clone());
+                    account_info.insert("access_key".to_string(), account[1].clone());
+                    account_info.insert("secret_key".to_string(), account[2].clone());
+                    info!("账号:{:?}",account_info);
+
+                    let mut gate_exc = BybitSwapRest::new(false, account_info.clone());
+                    let data = gate_exc.get_account_transaction_log().await;
+                    // let data = gate_exc.get_position_closed_pnl("BSVUSDT".to_string()).await;
+                    // let data = gate_exc.get_account_wallet_fund_records(0, 0).await;
+
+                    // info!("请求完成{:?}",data.clone());
+                    if data.code.as_str() == "200" {
+                        let account_name = account_info.get("account_name").unwrap();
+                        account_name_list.push(account_name.clone());
+
+                        //解析
+                        let json_value: serde_json::Value = serde_json::from_str(&data.data).unwrap();
+                        let ret_code = json_value.get("retCode").unwrap().as_i64().unwrap();
+                        let ret_msg = json_value.get("retMsg").unwrap().as_str().unwrap();
+                        if ret_code == 0 && ret_msg == "OK" {
+                            let result = json_value.get("result").unwrap();
+                            let list = result.get("list").unwrap();
+                            if let serde_json::Value::Array(array) = list {
+                                let mut name_data_all: Vec<Vec<String>> = vec![];
+
+                                for item in array {
+                                    // info!("数据{:?}",item.clone().to_string());
+
+                                    let id = item.get("id").unwrap().as_str().unwrap();
+                                    let type_str = item.get("type").unwrap().as_str().unwrap();
+                                    let b_symbol = item.get("symbol").unwrap().as_str().unwrap();
+                                    let side = item.get("side").unwrap().as_str().unwrap();
+                                    let currency = item.get("currency").unwrap().as_str().unwrap();
+                                    let tradePrice = item.get("tradePrice").unwrap().as_str().unwrap();
+                                    let funding = item.get("funding").unwrap().as_str().unwrap();
+                                    let fee = item.get("fee").unwrap().as_str().unwrap();
+                                    let cashFlow = item.get("cashFlow").unwrap().as_str().unwrap();
+                                    let change = item.get("change").unwrap().as_str().unwrap();
+                                    let cashBalance = item.get("cashBalance").unwrap().as_str().unwrap();
+                                    let transactionTime = item.get("transactionTime").unwrap().as_str().unwrap();
+
+                                    let type_str = match type_str {
+                                        "TRADE" => { "交易" }
+                                        _ => {
+                                            "其他-变更类型";
+                                            continue;
+                                        }
+                                    };
+
+                                    // info!("result 数据\
+                                    //  transactionTime:{:?} id:{:?},symbol:{:?},side:{:?},currency:{:?},tradePrice:{:?},funding:{:?},fee:{:?},cashFlow:{:?},cashBalance:{:?},change:{:?}"
+                                    //     ,transactionTime, id,b_symbol,side,currency,tradePrice,funding,fee,cashFlow,cashBalance,change);//change 變更 = cashFlow + funding - fee
+
+//                                     "{\"bonusChange\":\"0\",\"cashBalance\":\"97.95598152\",\"cashFlow\":\"0\",\"category\":\"linear\",\"change\":\"-0.01119174\",\"currency\":\"USDT\",\"fee\":\"0.01119174
+// \",\"feeRate\":\"0.00014\",\"funding\":\"0\",\"id\":\"123105018_INJUSDT_618678386870\",\"orderId\":\"3d3da36b-c803-441a-bb0b-24fdef56d6f3\",\"orderLinkId\":\"t-459097906\",\"qty\":\"2\",\"side\":\"Buy\",\"size\":\"2\",\"symbol\"
+// :\"INJUSDT\",\"tradeId\":\"ed458708-3de4-5bc8-bc27-f6b4ba7a8921\",\"tradePrice\":\"39.9705\",\"transactionTime\":\"1703224750479\",\"type\":\"TRADE\"}"
+
+//              "{\"balance\":\"49.051791949878\",\"change\":\"-0.0576\",\"contract\":\"OP_USDT\",\"text\":\"OP_USDT:376554197685\",\"time\":1702110912,\"trade_id\":\"16306596\",\"type
+// \":\"pnl\"}"
 
+                                    let time = transactionTime.parse::<i64>().unwrap() / 1000;
 
-                        let text = item["text"].as_str().unwrap();
-                        let contract = item["contract"].as_str().unwrap();
-                        let trade_id = item["trade_id"].as_str().unwrap();
+                                    let mut name_data_array: Vec<String> = vec![];
+                                    name_data_array.push(time.to_string());
+                                    name_data_array.push(id.to_string());
+                                    name_data_array.push(change.to_string());
+                                    name_data_array.push(cashBalance.to_string());
+                                    name_data_array.push(type_str.to_string());
+                                    name_data_array.push("".to_string());
+                                    name_data_array.push("".to_string());
 
-                        let mut name_data_array: Vec<String> = vec![];
-                        name_data_array.push(time.to_string());
-                        name_data_array.push(trade_id.to_string());
-                        name_data_array.push(change.to_string());
-                        name_data_array.push(balance.to_string());
-                        name_data_array.push(type_str.to_string());
-                        name_data_array.push(contract.to_string());
-                        name_data_array.push(text.to_string());
+                                    name_data_all.push(name_data_array.clone());
+                                }
 
-                        name_data_all.push(name_data_array.clone());
+                                all_account_balance_info.insert(account_name.clone(), name_data_all.clone());
+                            }
+                        } else {
+                            info!("解析失败:{:?}",json_value);
+                            continue;
+                        }
                     }
-                    all_account_balance_info.insert(account_name.clone(), name_data_all.clone());
-                } else {
-                    info!("不是数组 检查数据");
+                    // break;
                 }
             }
-            // break;
-        }
-        break;//这里是为了 代码收纳,用了loop来放置代码
+            _ => {
+                error!("交易所输入错误!");
+                panic!("交易所输入错误!")
+            }
+        };
     }
+    info!("all_account_balance_info:{:?}",all_account_balance_info);
+
 
     // 根据账户变动填充
     let mut count_time_list = vec![];
@@ -139,6 +237,7 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
     hour_time_list.push(end_time);
     let all_balance_info: Vec<Vec<String>> = supply_balance(all_account_balance_info.clone(), hour_time_list.clone());
 
+    account_name_list.push("total_balance".to_string());
     account_name_list.push("total_balance".to_string());
     match config_info_clone.export_mode {
         1 => export_template::template_balance::export_html(config_info, &account_name_list, &hour_time_list, &all_balance_info, statistic_result),
@@ -160,34 +259,25 @@ pub fn statistic_balance(balance_info: Vec<Vec<String>>, count_start_time: i64,
         balance_info_map.insert(info[0].clone(), default_info);
     }
     for (account_name, balance_info) in balance_info_map.clone() {
-        let mut max_price = dec!(0);
-        let mut min_price = dec!(0);
-        let mut withdrawal = dec!(0);
         for (index, info) in balance_info.iter().enumerate() {
-            let present_price = Decimal::from_str(&info[4]).unwrap();
-            if index == 0 || max_price < present_price {
-                max_price = present_price;
-                min_price = present_price;
-                continue;
-            }
-            let present_withdrawal = ((Decimal::ONE - (min_price / max_price)) * dec!(100)).round_dp(2);
-            if present_withdrawal > withdrawal { withdrawal = present_withdrawal }
-
-            if max_price > present_price && present_price < min_price {
-                min_price = present_price;
+            if index == 0 { continue; }
+            let present = Decimal::from_str(&info[4]).unwrap();
+            let past = Decimal::from_str(&balance_info[index - 1][4]).unwrap();
+            let withdrawal = ((Decimal::ONE - (present / past)) * dec!(100)).round_dp(2);
+            if account_name != "total_balance" {
+                if max_withdrawal < withdrawal { max_withdrawal = withdrawal }
+            } else {
+                if max_total_withdrawal < withdrawal { max_total_withdrawal = withdrawal }
             }
         }
-        if account_name != "total_balance" {
-            if max_withdrawal < withdrawal { max_withdrawal = withdrawal }
-        } else {
-            max_total_withdrawal = withdrawal;
+        if account_name == "total_balance" {
             let present = Decimal::from_str(&balance_info[0][4]).unwrap();
             let past = Decimal::from_str(&balance_info[balance_info.len() - 1][4]).unwrap();
             total_income = (((past / present) - Decimal::ONE) * dec!(100)).round_dp(2);
         }
     }
-    let start_time = NaiveDateTime::from_timestamp_millis((count_start_time + 8 * 3600) * 1000).unwrap().format("%d日%H点").to_string();
-    let end_time = NaiveDateTime::from_timestamp_millis((end_start_time + 8 * 3600) * 1000).unwrap().format("%d日%H点").to_string();
+    let start_time = NaiveDateTime::from_timestamp_millis((count_start_time + 8 * 3600) * 1000).unwrap().format("%Y-%m-%d日%H点").to_string();
+    let end_time = NaiveDateTime::from_timestamp_millis((end_start_time + 8 * 3600) * 1000).unwrap().format("%Y-%m-%d日%H点").to_string();
     format!("{}到{},总收益约为{}%,最大回撤约为{}%,其中单个账号最大回撤约为{}%。", start_time, end_time, total_income, max_total_withdrawal, max_withdrawal)
 }
 
@@ -225,7 +315,13 @@ pub fn supply_balance(source_balance_info: BTreeMap<String, Vec<Vec<String>>>, t
                     let balance_info_filter: Vec<Vec<String>> = balance_info.iter().filter(|item| {
                         item[0].parse::<i64>().unwrap() < time
                     }).cloned().collect();
-                    let mut last_balance_info = balance_info_filter[balance_info_filter.len() - 1].clone();
+                    info!("balance_info_filter:{:?}",balance_info_filter.clone());
+                    let mut last_balance_info = vec![];
+                    if balance_info_filter.len() == 0{
+                        last_balance_info = balance_info[0].clone();
+                    }else{
+                        last_balance_info = balance_info_filter[balance_info_filter.len() - 1].clone();
+                    }
                     last_balance_info.insert(0, key.clone());
                     total_price += Decimal::from_str(&last_balance_info[4]).unwrap();
                     balance_info_list.push(last_balance_info);

+ 3 - 2
src/main.rs

@@ -12,7 +12,8 @@ pub mod struct_standard;
 pub mod export_template;
 pub mod export_balance;
 pub mod export_ticker;
-mod hl_pr_utile;
+pub mod hl_pr_utile;
+pub mod swap_bybit;
 
 struct ConfigList {
     ticker_info: TickerConfigInfo,
@@ -22,7 +23,7 @@ struct ConfigList {
 #[tokio::main]
 async fn main() {
     logs::init_log_with_info();
-    let ticker_config = utils::utils::get_ticker_config_info("./config_ticker.toml");
+    let ticker_config = utils::utils::get_ticker_config_info("./config_ticker1.toml");
     let balance_config = utils::utils::get_balance_config_info("./config_balance.toml");
     let config_obj = ConfigList {
         ticker_info: ticker_config,

+ 26 - 0
src/swap_bybit/bybit_swap_rest.rs

@@ -0,0 +1,26 @@
+use crate::http::request::{get, Response};
+
+pub async fn get_history_trades(symbol: &str, start_at: &str, end_at: &str) -> Response {
+    let url = "https://www.okx.com/api/v5/market/history-trades";
+    let mut params = serde_json::json!({
+        "instId":symbol,
+        "type":"2"
+    });
+    if end_at.len() > 0 {
+        params["after"] = serde_json::Value::from(end_at);
+    }
+    if start_at.len() > 0 {
+        params["before"] = serde_json::Value::from(start_at);
+    }
+    get(url, params.to_string(), None).await
+}
+
+
+//查阅所有交易币对的 基础信息
+pub async fn get_symbol_details_all() -> Response {
+    let url = "https://www.okx.com/api/v5/public/instruments";
+    let params = serde_json::json!({
+        "instType":"SWAP"
+    });
+    get(url, params.to_string(), None).await
+}

+ 395 - 0
src/swap_bybit/bybit_swap_rest_utils.rs

@@ -0,0 +1,395 @@
+use std::collections::BTreeMap;
+
+use reqwest::Client;
+use reqwest::header::HeaderMap;
+use ring::hmac;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use rust_decimal_macros::dec;
+use toml::Value;
+use tracing::{info, trace};
+use crate::http::request::Response;
+use crate::utils::utils::parse_params_to_str;
+
+
+#[derive(Clone, Debug)]
+pub struct BybitSwapRest {
+    pub label: String,
+    base_url: String,
+    client: reqwest::Client,
+    /*******参数*/
+    //登陆所需参数
+    login_param: BTreeMap<String, String>,
+    delays: Vec<i64>,
+    max_delay: i64,
+    avg_delay: Decimal,
+}
+
+impl BybitSwapRest {
+    /*******************************************************************************************************/
+    /*****************************************获取一个对象****************************************************/
+    /*******************************************************************************************************/
+
+    pub fn new(is_colo: bool, login_param: BTreeMap<String, String>) -> BybitSwapRest
+    {
+        return BybitSwapRest::new_label("default-BybitSwapRest".to_string(), is_colo, login_param);
+    }
+    pub fn new_label(label: String, is_colo: bool, login_param: BTreeMap<String, String>) -> BybitSwapRest {
+        let base_url = if is_colo {
+            "https://api.bytick.com".to_string()
+        } else {
+            "https://api.bytick.com".to_string()
+        };
+
+        if is_colo {
+            info!("开启高速(未配置,走普通:{})通道",base_url);
+        } else {
+            info!("走普通通道:{}",base_url);
+        }
+        /*****返回结构体*******/
+        BybitSwapRest {
+            label,
+            base_url,
+            client: Client::new(),
+            login_param,
+            delays: vec![],
+            max_delay: 0,
+            avg_delay: dec!(0.0),
+        }
+    }
+
+    /*******************************************************************************************************/
+    /*****************************************rest请求函数********************************************************/
+    /*******************************************************************************************************/
+    //服務器時間
+    pub async fn get_server_time(&mut self) -> Response {
+        let params = serde_json::json!({
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/market/time".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //查詢可交易產品的規格信息
+    pub async fn get_instruments_info(&mut self, symbol: String) -> Response {
+        let params = serde_json::json!({
+            "category":"linear",
+            "symbol":symbol
+         });
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/market/instruments-info".to_string(),
+                                false,
+                                params.to_string(),
+        ).await;
+        data
+    }
+    //賬戶的交易日誌。
+    pub async fn get_account_transaction_log(&mut self) -> Response {
+        let mut params = serde_json::json!({
+            "accountType":"UNIFIED",
+            "category":"linear",
+            "currency":"USDT",
+         });
+        params["startTime"] = serde_json::json!(1703124000000 as i64);
+        params["endTime"] = serde_json::json!(1703664000000  as i64);
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/account/transaction-log".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查詢平倉盈虧
+    pub async fn get_position_closed_pnl(&mut self, symbol: String) -> Response {
+        let mut params = serde_json::json!({
+            "symbol":symbol,
+            "limit":"200"
+         });
+        params["startTime"] = serde_json::json!(1703124000000 as i64);
+        params["endTime"] = serde_json::json!(1703664000000  as i64);
+        let data = self.request("GET".to_string(),
+                                "/contract/v3".to_string(),
+                                "/private/position/closed-pnl".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+    //查詢錢包資金紀錄
+    pub async fn get_account_wallet_fund_records(&mut self, start_time: i64, end_time: i64) -> Response {
+        let mut params = serde_json::json!({
+            "coin":"USDT",
+            "walletFundType":"RealisedPNL",
+            "limit":"50",
+         });
+        // if start_time > 0 {
+        params["startTime"] = serde_json::json!(1703124000000 as i64);
+        // }
+        // if end_time > 0 {
+        params["endTime"] = serde_json::json!(1703664000000  as i64);
+        // }
+
+        let data = self.request("GET".to_string(),
+                                "/contract/v3".to_string(),
+                                "/private/account/wallet/fund-records".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
+
+
+    /*******************************************************************************************************/
+    /*****************************************工具函数********************************************************/
+    /*******************************************************************************************************/
+    pub fn get_delays(&self) -> Vec<i64> {
+        self.delays.clone()
+    }
+    pub fn get_avg_delay(&self) -> Decimal {
+        self.avg_delay.clone()
+    }
+    pub fn get_max_delay(&self) -> i64 {
+        self.max_delay.clone()
+    }
+    fn get_delay_info(&mut self) {
+        let last_100 = if self.delays.len() > 100 {
+            self.delays[self.delays.len() - 100..].to_vec()
+        } else {
+            self.delays.clone()
+        };
+
+        let max_value = last_100.iter().max().unwrap();
+        if max_value.clone().to_owned() > self.max_delay {
+            self.max_delay = max_value.clone().to_owned();
+        }
+
+        let sum: i64 = last_100.iter().sum();
+        let sum_v = Decimal::from_i64(sum).unwrap();
+        let len_v = Decimal::from_u64(last_100.len() as u64).unwrap();
+        self.avg_delay = (sum_v / len_v).round_dp(1);
+        self.delays = last_100.clone().into_iter().collect();
+    }
+    //调用请求
+    pub async fn request(&mut self,
+                         method: String,
+                         prefix_url: String,
+                         request_url: String,
+                         is_login: bool,
+                         params: String) -> Response
+    {
+        trace!("login_param:{:?}", self.login_param);
+        //解析账号信息
+        let mut access_key = "".to_string();
+        let mut secret_key = "".to_string();
+        // let mut passphrase = "".to_string();
+        if self.login_param.contains_key("access_key") {
+            access_key = self.login_param.get("access_key").unwrap().to_string();
+        }
+        if self.login_param.contains_key("secret_key") {
+            secret_key = self.login_param.get("secret_key").unwrap().to_string();
+        }
+        // if self.login_param.contains_key("pass_key") {
+        //     passphrase = self.login_param.get("pass_key").unwrap().to_string();
+        // }
+        let mut is_login_param = true;
+        if access_key == "" || secret_key == "" {
+            is_login_param = false
+        }
+
+
+        //请求头配置-如果需要登陆则存在额外配置
+        let mut body = "".to_string();
+        let timestamp = Self::get_timestamp();
+        let mut headers = HeaderMap::new();
+        headers.insert("Content-Type", "application/json; charset=utf-8".parse().unwrap());
+        headers.insert("X-BAPI-RECV-WINDOW", "5000".parse().unwrap());
+
+        if method == "POST" {
+            body = params.clone();
+        }
+
+
+        //是否需要登陆-- 组装sing
+        if is_login {
+            if !is_login_param {
+                let mut res = Response::new();
+                res.code = "-1".to_string();
+                res.msg = "登陆参数错误".to_string();
+                return res;
+            } else {
+                //需要登陆-且登陆参数齐全
+                trace!("param:{}", params);
+                trace!("body:{}", body);
+                //组装sing
+                let sing = Self::sign(
+                    access_key.clone(),
+                    secret_key.clone(),
+                    method.clone(),
+                    params.clone(),
+                    timestamp.clone(),
+                );
+                //组装header
+                headers.extend(Self::headers(sing, timestamp, access_key));
+            }
+        }
+
+
+        // trace!("headers:{:?}", headers);
+        let base_url = format!("{}{}", prefix_url.clone(), request_url.clone());
+        let start_time = chrono::Utc::now().timestamp_millis();
+        let get_response = self.http_toll(
+            format!("{}{}", prefix_url.clone(), request_url.clone()),
+            method.to_string(),
+            params.clone(),
+            headers,
+        ).await;
+        let time_array = chrono::Utc::now().timestamp_millis() - start_time;
+        self.delays.push(time_array);
+        self.get_delay_info();
+        let res_data = Self::res_data_analysis(get_response, base_url, params);
+        res_data
+    }
+
+    pub fn headers(sign: String, timestamp: String, access_key: String) -> HeaderMap {
+        let mut headers = HeaderMap::new();
+        headers.insert("X-BAPI-SIGN-TYPE", "2".parse().unwrap());
+        headers.insert("X-BAPI-API-KEY", access_key.parse().unwrap());
+        headers.insert("X-BAPI-TIMESTAMP", timestamp.parse().unwrap());
+        headers.insert("X-BAPI-SIGN", sign.parse().unwrap());
+        // headers.insert("X-Referer", passphrase.parse().unwrap());
+        headers
+    }
+    pub fn sign(access_key: String,
+                secret_key: String,
+                method: String,
+                params: String, timestamp: String) -> String
+    {
+        /*签名生成*/
+        let url_param_str = parse_params_to_str(params.clone());
+        let parameters = if method == "GET" {
+            url_param_str
+        } else {
+            params
+        };
+
+        let message = format!("{}{}5000{}", timestamp, access_key, parameters);
+        trace!("message:{}",message);
+
+        // 做签名
+        let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+        let result = ring::hmac::sign(&hmac_key, &message.as_bytes());
+        let sign = hex::encode(result.as_ref());
+        sign
+    }
+
+    async fn http_toll(&mut self, request_path: String, request_type: String, params: String, headers: HeaderMap) -> Result<Response, reqwest::Error> {
+        /****请求接口与 地址*/
+        let url = format!("{}{}", self.base_url.to_string(), request_path);
+        let request_type = request_type.clone().to_uppercase();
+        let addrs_url = format!("{}?{}", url.clone(), parse_params_to_str(params.clone()));
+        // let addrs_url: String = if  parse_params_to_str(params.clone()) == "" {
+        //     url.clone()
+        // } else {
+        //     format!("{}?{}", url.clone(), RestTool::parse_params_to_str(params.clone()))
+        // };
+
+        trace!("url:{}", url);
+        trace!("addrs_url:{}", addrs_url);
+        let params_json: serde_json::Value = serde_json::from_str(&params).unwrap();
+        trace!("params_json:{}",params_json);
+        trace!("headers:{:?}",headers);
+
+
+        let req = match request_type.as_str() {
+            "GET" => self.client.get(addrs_url.clone()).headers(headers),
+            "POST" => self.client.post(url.clone()).body(params).headers(headers),
+            "DELETE" => self.client.delete(addrs_url.clone()).headers(headers),
+            // "PUT" => self.client.put(url.clone()).json(&params),
+            // _ => return Ok(ResponseData::error(self.label.clone(), format!("错误的请求类型:{}", request_type.clone()))), // 处理未知请求类型
+            _ => {
+                let mut res = Response::new();
+                res.code = "-1".to_string();
+                res.msg = "请求类型错误".to_string();
+                res.data = "".to_string();
+                return Ok(res);
+            }
+        };
+
+        let response = req.send().await?;
+        let mut res = Response::new();
+        if response.status().is_success() {
+            // 读取响应的内容
+            let body = response.text().await?;
+            trace!("ok-----{}", body);
+            // res_data = ResponseData::new(self.label.clone(), "200".to_string(), "success".to_string(), body);
+            res.code = "200".to_string();
+            res.msg = "success".to_string();
+            res.data = body;
+        } else {
+            let body = response.text().await?;
+            trace!("error-----{}", body);
+            // res_data = ResponseData::error(self.label.clone(), body.to_string())
+            res.code = "-1".to_string();
+            res.msg = body;
+            res.data = "".to_string();
+        }
+
+        Ok(res)
+    }
+
+
+    //res_data 解析
+    pub fn res_data_analysis(result: Result<Response, reqwest::Error>, base_url: String, params: String) -> Response {
+        let res = Response::new();
+        trace!("原始数据:{:?}",result);
+        match result {
+            Ok(mut res_data) => {
+                if res_data.code != "200" {
+                    // res_data
+                    trace!("不等于200");
+                    let msg = res_data.msg;
+                    let body: String = res_data.data.as_str().parse().unwrap();
+                    let json_value = serde_json::from_str::<Value>(&body);
+
+                    match json_value {
+                        Ok(_data) => {
+                            let mut error = Response::new();
+                            error.code = "-1".to_string();
+                            error.msg = format!("请求错误{:?}", res.msg);
+                            error.data = format!("请求地址:{},请求参数:{}", base_url, params);
+                            error
+                        }
+                        Err(e) => {
+                            let mut error = Response::new();
+                            error.code = "-1".to_string();
+                            error.msg = format!("json 解析失败:{},相关参数:{}", e, msg);
+                            error.data = "".to_string();
+                            error
+                        }
+                    }
+                } else {
+                    res_data
+                }
+            }
+            Err(err) => {
+                let mut error = Response::new();
+                error.code = "-1".to_string();
+                error.msg = format!("json 解析失败:{},相关参数:{}", err, params);
+                error.data = "".to_string();
+                error
+            }
+        }
+    }
+
+    fn get_timestamp() -> String {
+        chrono::Utc::now().timestamp_millis()
+            .to_string()
+    }
+}

+ 183 - 0
src/swap_bybit/bybit_swap_standard.rs

@@ -0,0 +1,183 @@
+use std::io::{Error, ErrorKind};
+use rust_decimal::Decimal;
+
+use serde::{Deserialize, Serialize};
+
+use crate::swap_okx::okx_swap_rest::{get_history_trades, get_symbol_details_all};
+use crate::struct_standard::Trades;
+
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SwapTrades {
+    inst_id: String,
+    //	产品ID
+    trade_id: String,
+    //	成交ID
+    px: String,
+    //	成交价格
+    sz: Decimal,
+    //成交数量
+    side: String,
+    //成交方向buy:,//买sell:,//卖
+    ts: String,//成交时间,Unix时间戳的毫秒数格式, 如1597026383085
+}
+
+pub(crate) async fn standard_history_candles(symbol: &str, start_at: &str, end_at: &str, ct_val: Decimal) -> Result<Vec<Trades>, Error> {
+    let mut symbol_fmt = format!("{}-SWAP", symbol.replace("_", "-").clone());
+    symbol_fmt = symbol_fmt.to_uppercase();
+
+
+    let res_data = get_history_trades(&symbol_fmt, &start_at, &end_at).await;
+    if res_data.code == "200" {
+        let res_data_str = res_data.data;
+        let json_value: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+
+        let result = if json_value.get("code").is_some() && json_value["code"] == "0" {
+            let data = json_value.get("data").unwrap();
+
+            // info!("data:{:?}",data.to_string().as_str());
+
+            let ticker_list: Vec<SwapTrades> = serde_json::from_str(data.to_string().as_str()).unwrap();
+            ticker_list.iter().map(|item| {
+                let mut size_i = item.sz;
+                if item.side.as_str() == "sell" {
+                    size_i = -size_i;
+                } else if item.side.as_str() == "buy" {
+                    size_i = size_i;
+                }
+
+                Trades {
+                    id: item.trade_id.to_string(),
+                    data_type: "ticker_okx".to_string(),
+                    symbol: symbol.to_string(),
+                    create_time: item.ts.to_string(),
+                    size: (size_i * ct_val).to_string(),
+                    price: item.px.clone(),
+                    side: if size_i > Decimal::ZERO { "BUY".to_string() } else { "SELL".to_string() },
+                    is_effect: true,
+                }
+            }).collect()
+        } else {
+            vec![]
+        };
+        Ok(result)
+    } else {
+        Err(Error::new(ErrorKind::Other, res_data.to_string()))
+    }
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone)]
+#[serde(rename_all = "camelCase")]
+#[allow(non_snake_case)]
+pub struct SwapSymbolDateils {
+    instType: String,
+    //    产品类型
+    instId: String,
+    //    产品id, 如 BTC-USD-SWAP
+    uly: String,
+    //    标的指数,如 BTC-USD,仅适用于交割/永续/期权
+    instFamily: String,
+    //    交易品种,如 BTC-USD,仅适用于交割/永续/期权
+    category: String,
+    //    币种类别(已废弃)
+    baseCcy: String,
+    //   交易货币币种,如 BTC-USDT 中的 BTC ,仅适用于币币/币币杠杆
+    quoteCcy: String,
+    //   计价货币币种,如 BTC-USDT 中的USDT ,仅适用于币币/币币杠杆
+    settleCcy: String,
+    //   盈亏结算和保证金币种,如 BTC 仅适用于交割/永续/期权
+    ctVal: Decimal,
+    //  合约面值,仅适用于交割/永续/期权
+    ctMult: String,
+    //   合约乘数,仅适用于交割/永续/期权
+    ctValCcy: String,
+    //  合约面值计价币种,仅适用于交割/永续/期权
+    optType: String,
+    //   期权类型,C或P 仅适用于期权
+    stk: String,
+    //  行权价格,仅适用于期权
+    listTime: String,
+    //  上线时间   Unix时间戳的毫秒数格式,如 1597026383085
+    expTime: String,
+    //   产品下线时间   适用于币币/杠杆/交割/永续/期权,对于 交割/期权,为交割/行权日期;亦可以为产品下线时间,有变动就会推送。
+    lever: String,
+    //   该instId支持的最大杠杆倍数,不适用于币币、期权
+    tickSz: String,
+    //   下单价格精度,如 0.0001     对于期权来说,是梯度中的最小下单价格精度,如果想要获取期权价格梯度,请使用"获取期权价格梯度"接口
+    lotSz: String,
+    //   下单数量精度,如 BTC-USDT-SWAP:1
+    minSz: String,
+    //   最小下单数量,    合约的数量单位是“张”,现货的数量单位是“交易货币”
+    ctType: String,
+    //  linear:正向合约 inverse:反向合约   仅适用于交割/永续
+    alias: String,
+    //    合约日期别名
+    // this_week:本周
+    // next_week:次周
+    // this_month:本月
+    // next_month:次月
+    // quarter:季度
+    // next_quarter:次季度
+    // 仅适用于交割
+    // 不建议使用,用户应通过 expTime 字段获取合约的交割日期
+    state: String,
+    //     产品状态
+    // live:交易中
+    // suspend:暂停中
+    // preopen:预上线,如:交割和期权的新合约在 live 之前,会有 preopen 状态
+    // test:测试中(测试产品,不可交易)
+    maxLmtSz: String,
+    //    合约或现货限价单的单笔最大委托数量,   合约的数量单位是“张”,现货的数量单位是“交易货币”
+    maxMktSz: String,
+    //    合约或现货市价单的单笔最大委托数量,   合约的数量单位是“张”,现货的数量单位是“USDT”
+    maxLmtAmt: String,
+    //    限价单的单笔最大USD价值
+    maxMktAmt: String,
+    //    市价单的单笔最大USD价值    仅适用于币币/币币杠杆
+    maxTwapSz: String,
+    //    合约或现货时间加权单的单笔最大委托数量,  合约的数量单位是“张”,现货的数量单位是“交易货币”
+    maxIcebergSz: String,
+    //    合约或现货冰山委托的单笔最大委托数量,  合约的数量单位是“张”,现货的数量单位是“交易货币”
+    maxTriggerSz: String,
+    //     合约或现货计划委托委托的单笔最大委托数量,   合约的数量单位是“张”,现货的数量单位是“交易货币”
+    maxStopSz: String,//     合约或现货止盈止损市价委托的单笔最大委托数量,   合约的数量单位是“张”,现货的数量单位是“USDT”
+}
+
+pub(crate) async fn standard_symbol_details(symbol: &str) -> Result<Option<SwapSymbolDateils>, Error> {
+    let mut symbol_fmt = symbol.replace("_", "-").clone();
+    symbol_fmt = symbol_fmt.to_uppercase();
+    let ct_val_ccy_array: Vec<&str> = symbol_fmt.split("-").collect();
+    let symbol_name = ct_val_ccy_array[0];
+
+
+    let res_data = get_symbol_details_all().await;
+    if res_data.code == "200" {
+        let res_data_str = res_data.data;
+        let json_value: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+
+        if json_value.get("code").is_some() && json_value["code"] == "0" {
+            let data = json_value.get("data").unwrap();
+            // info!("data:{:?}",data.to_string().as_str());
+            let symbol_dateils_all: Vec<SwapSymbolDateils> = serde_json::from_str(data.to_string().as_str()).unwrap();
+
+            for symbol_dateils in symbol_dateils_all {
+                if symbol_name == symbol_dateils.ctValCcy.as_str() {
+                    return Ok(Option::from(symbol_dateils));
+                }
+            }
+        }
+        Ok(None)
+    } else {
+        Err(Error::new(ErrorKind::Other, res_data.to_string()))
+    }
+}
+
+pub async fn standard_ct_val(symbol: &str) -> Decimal {
+    match standard_symbol_details(symbol).await.unwrap() {
+        None => Decimal::ONE,
+        Some(d) => d.ctVal
+    }
+}
+
+
+

+ 3 - 0
src/swap_bybit/mod.rs

@@ -0,0 +1,3 @@
+pub mod bybit_swap_rest;
+pub mod bybit_swap_standard;
+pub mod bybit_swap_rest_utils;