Browse Source

完成balance导出整理

gepangpang 1 year ago
parent
commit
e2fc3a110d
4 changed files with 113 additions and 95 deletions
  1. 3 1
      config_balance.toml.sample
  2. 102 93
      src/export_balance.rs
  3. 6 1
      src/export_template/template_balance.rs
  4. 2 0
      src/utils/utils.rs

+ 3 - 1
config_balance.toml.sample

@@ -7,7 +7,9 @@ account_list = [["account_name", "access_key", "secret_key", "pass_key"]]
 # 配置交易所 目前支持["gate"]
 exchanges = ["gate"]
 # 配置查询时间(格式:2023-12-6 15:00:00)
-range_time = ["2023-12-13 11:24:00", "2023-12-13 11:24:50"]
+range_time = ["2023-12-20 18:00:00", "2023-12-26 10:00:00"]
+# 配置查询时间(格式:2023-12-6 15:00:00)
+count_range_time = ["2023-12-25 10:00:00", "2023-12-26 10:00:00"]
 # 导出路径(不填则为默认路径"./")
 export_path = ""
 # 导出文件名字(不填则为默认名字"export")

+ 102 - 93
src/export_balance.rs

@@ -2,6 +2,7 @@ use std::collections::{BTreeMap};
 use std::str::FromStr;
 use chrono::NaiveDateTime;
 use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
 use tracing::info;
 use crate::{export_template};
 use crate::swap_gate::gate_swap_rest_utils::GateSwapRest;
@@ -11,10 +12,11 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
     let config_info_clone = config_info.clone();
     let start_time = NaiveDateTime::parse_from_str(&config_info.range_time[0].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
     let end_time = NaiveDateTime::parse_from_str(&config_info.range_time[1].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
+    let count_start_time = NaiveDateTime::parse_from_str(&config_info.count_range_time[0].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
+    let count_end_time = NaiveDateTime::parse_from_str(&config_info.count_range_time[1].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
 
     //获取不同账号的数据
-    let mut acc_all_data: BTreeMap<String, Vec<Vec<String>>> = BTreeMap::new();
-    let mut data_array_all: Vec<Vec<String>> = vec![];
+    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 {
@@ -50,9 +52,7 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
 
                         let balance = item["balance"].as_str().unwrap();
                         let type_str = match item["type"].as_str().unwrap() {
-                            "dnw" => {
-                                "转入转出"
-                            }
+                            "dnw" => { "转入转出" }
                             "pnl" => { "减仓盈亏" }
                             "fee" => {
                                 "交易手续费";
@@ -100,7 +100,7 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
 
                         name_data_all.push(name_data_array.clone());
                     }
-                    acc_all_data.insert(account_name.clone(), name_data_all.clone());
+                    all_account_balance_info.insert(account_name.clone(), name_data_all.clone());
                 } else {
                     info!("不是数组 检查数据");
                 }
@@ -110,7 +110,23 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
         break;//这里是为了 代码收纳,用了loop来放置代码
     }
 
-    // 按小时取时间片
+    // 根据账户变动填充
+    let mut count_time_list = vec![];
+    for (_account_name, balance_info) in all_account_balance_info.clone() {
+        for value in balance_info {
+            let time = value[0].clone().parse::<i64>().unwrap();
+            if !count_time_list.contains(&time) && time > count_start_time && time < count_end_time {
+                count_time_list.push(time)
+            }
+        }
+    };
+    count_time_list.sort_by(|a, b| a.cmp(&b));
+    count_time_list.insert(0, count_start_time);
+    count_time_list.push(count_end_time);
+    let count_balance_info = supply_balance(all_account_balance_info.clone(), count_time_list.clone());
+    let statistic_result = statistic_balance(count_balance_info.clone(), count_start_time.clone(), count_end_time.clone());
+
+    // 根据小时填充
     let mut last_timestamp: i64 = -1;
     let mut export_time_list: Vec<i64> = vec![];
     for item in start_time..end_time {
@@ -120,102 +136,95 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
         }
     }
     export_time_list.push(end_time);
+    let all_balance_info: Vec<Vec<String>> = supply_balance(all_account_balance_info.clone(), export_time_list.clone());
+
+    account_name_list.push("total_balance".to_string());
+    export_template::template_balance::export_html(config_info_clone.clone(), &account_name_list, &export_time_list, &all_balance_info, statistic_result);
+}
+
 
-    //2. 数据根据时间片 补全数据
-    for time in &export_time_list {
+pub fn statistic_balance(balance_info: Vec<Vec<String>>, count_start_time: i64, end_start_time: i64) -> String {
+    let mut max_withdrawal = dec!(0);
+    let mut max_total_withdrawal = dec!(0);
+    let mut total_income = dec!(0);
+
+    let mut balance_info_map: BTreeMap<String, Vec<Vec<String>>> = BTreeMap::new();
+    for info in balance_info.clone() {
+        let mut default_info = balance_info_map.get(&info[0]).unwrap_or(&vec![]).clone();
+        default_info.push(info.clone());
+        balance_info_map.insert(info[0].clone(), default_info);
+    }
+    for (account_name, balance_info) in balance_info_map.clone() {
+        for (index, info) in balance_info.iter().enumerate() {
+            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" {
+            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("%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)
+}
+
+pub fn supply_balance(source_balance_info: BTreeMap<String, Vec<Vec<String>>>, time_slicer: Vec<i64>) -> Vec<Vec<String>> {
+    let mut balance_info_list: Vec<Vec<String>> = vec![];
+    for time in time_slicer.clone() {
         // 根据时间片去拿数据 ,如果有添加没有,手动添加
-        let mut sum_pr = Decimal::ZERO;
-        for (key, vale) in acc_all_data.clone() {
-            let mut value = vale.clone();
-            value.sort_by(|a, b| a[0].cmp(&b[0]));
-            let mut da: Option<Vec<String>> = None;
-            for v in value.clone() {
-                let acc_time = v[0].parse::<i64>().unwrap();
-                if acc_time == time.clone() {
-                    //时间相同,如果记录的 时间片不存在,则直接保存,如果存在则比较成交id,
-                    if da.is_none() {
-                        da = Option::from(v);//记录当前实际
-                    } else {
-                        let da_id = da.clone().unwrap()[1].clone();
-                        let v_id = v[1].clone();
-                        if da_id < v_id {
-                            da = Option::from(v);//记录当前实际
+        let mut total_price = Decimal::ZERO;
+        for (key, value) in source_balance_info.clone() {
+            let mut balance_info = value.clone();
+            balance_info.sort_by(|a, b| a[0].cmp(&b[0]));
+
+            let mut new_info: Option<Vec<String>> = None;
+            for mut info in balance_info.clone() {
+                let info_time = info[0].parse::<i64>().unwrap();
+                if info_time == time {
+                    match new_info {
+                        None => {
+                            info.insert(0, key.clone());
+                            new_info = Option::from(info);//记录当前实际
+                        }
+                        Some(ref value) => {
+                            if value[1] < info[1] {
+                                info.insert(0, key.clone());
+                                new_info = Option::from(info);//记录当前实际
+                            }
                         }
                     }
+                    break;
                 }
             }
 
-            //如果匹配到时间片,直接使用,没有则创建
-            let new_d: Vec<String> = match da {
+            match new_info {
                 None => {
-                    let mut row: Vec<String> = vec![];
-                    let filter_arrya: Vec<Vec<String>> = value.clone().into_iter().filter(|s| {
-                        // info!("读取time:{:?}",s);
-                        let time_v = s[0].clone().parse::<i64>().unwrap();
-                        time_v < time.clone()
-                    }).collect();
-                    // info!("计算filter_arrya:---{:?}",filter_arrya.clone());
-                    let acc_name = key.clone().to_string();
-                    let acc_time = time.to_string();
-                    let acc_o_id = "填补数据类型".to_string();
-                    let mut acc_change = "0".to_string();
-                    let mut acc_balance = "0".to_string();
-                    let acc_type = "填补数据类型".to_string();
-                    let mut acc_contract = "填补数据类型".to_string();
-
-                    if filter_arrya.len() > 0 {
-                        // info!("{:?}--{}--{:?}",acc_name, time, filter_arrya[filter_arrya.len() - 1].clone());
-                        let zj_v = filter_arrya[filter_arrya.len() - 1].clone();
-
-                        acc_change = zj_v[2].clone();
-                        acc_balance = zj_v[3].clone();
-                        acc_contract = zj_v[5].clone();
-                    }
-
-                    let acc_balance_round = Decimal::from_str(&acc_balance).unwrap().round_dp(2);
-
-                    row.push(acc_name);
-                    row.push(acc_time);
-                    row.push(acc_o_id);
-                    row.push(acc_change);
-                    row.push(acc_balance_round.to_string());
-                    row.push(acc_type);
-                    row.push(acc_contract);
-                    row
+                    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();
+                    last_balance_info.insert(0, key.clone());
+                    total_price += Decimal::from_str(&last_balance_info[4]).unwrap();
+                    balance_info_list.push(last_balance_info);
                 }
-                Some(d) => {
-                    let mut row: Vec<String> = vec![];
-                    row.push(key.clone());
-                    row.extend(d.clone());
-                    row
+                Some(ref info) => {
+                    total_price += Decimal::from_str(&info[4]).unwrap();
+                    balance_info_list.push(info.clone());
                 }
-            };
-            // let time_str = NaiveDateTime::from_timestamp_millis((new_d[1].clone().parse::<i64>().unwrap() + 8 * 3600) * 1000).unwrap().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
-            // info!("{:?}----时间片:{:?}",new_d[0].clone(),time_str);
-            // info!("计算3333:---{:?}",time_str);
-            // info!("{:?}=----pr:{:?}",key.clone().to_string(),new_d[4].clone());
-            sum_pr = sum_pr + Decimal::from_str(&new_d[4]).unwrap();
-            data_array_all.push(new_d);
+            }
         }
 
-        let mut sum_row: Vec<String> = vec![];
-        sum_row.push("sum_acc".to_string());
-        sum_row.push(time.clone().to_string());
-        sum_row.push("".to_string());
-        sum_row.push("0".to_string());
-        sum_row.push(sum_pr.to_string());
-        sum_row.push("".to_string());
-        sum_row.push("".to_string());
-        data_array_all.push(sum_row.clone());
-    }
-
-    let data_array_all_array: Vec<Vec<String>> = data_array_all.clone().into_iter().filter(|value| {
-        let t = value[1].clone().parse::<i64>().unwrap();
-        t >= start_time && t <= end_time
-    }).collect();
-    data_array_all = data_array_all_array;
-
-    //生成 html
-    account_name_list.push("sum_acc".to_string());
-    export_template::template_balance::export_html(config_info_clone.clone(), &account_name_list, &export_time_list, &data_array_all);
+        let sum_row: Vec<String> = vec!["total_balance".to_string(), time.to_string(), "".to_string(), "0".to_string(), total_price.round_dp(2).to_string(), "".to_string(), "".to_string(), "".to_string()];
+        balance_info_list.push(sum_row.clone());
+    };
+    balance_info_list
 }

+ 6 - 1
src/export_template/template_balance.rs

@@ -8,7 +8,7 @@ use tracing::info;
 use crate::utils::utils::{BalanceConfigInfo};
 
 
-pub fn export_html(config: BalanceConfigInfo, acc_name_all: &Vec<String>, x_time: &Vec<i64>, data_list: &Vec<Vec<String>>) {
+pub fn export_html(config: BalanceConfigInfo, acc_name_all: &Vec<String>, x_time: &Vec<i64>, data_list: &Vec<Vec<String>>, statistic_result: String) {
     info!("正在生成网页,请稍后!");
     let export_path = if config.export_path == "" { "./" } else { config.export_path.as_str() };
     let export_name = if config.export_name == "" { "export_balance" } else { config.export_name.as_str() };
@@ -63,6 +63,7 @@ pub fn export_html(config: BalanceConfigInfo, acc_name_all: &Vec<String>, x_time
     let data = serde_json::json!({
         "chart_title": format!("账户合约余额变更历史"),
         "acc_name_all": acc_name_all.clone(),
+        "statistic_result": statistic_result.clone()
     });
     // HTML 模板
     let template = r#"
@@ -84,10 +85,14 @@ pub fn export_html(config: BalanceConfigInfo, acc_name_all: &Vec<String>, x_time
                     width: calc(100vw - 100px);
                     height: calc(100vh - 100px);
                 }
+                .info_wp{
+                    padding: 0 40px;
+                }
             </style>
         </head>
         <body>
             <div id="main"></div>
+            <div class="info_wp">{{statistic_result}}</div>
         </body>
         <script>
             var exchangeColor = {binance: '#F4BC0C', gate: '#0068FF', okx: '#171F30'};

+ 2 - 0
src/utils/utils.rs

@@ -71,6 +71,7 @@ pub struct BalanceConfigInfo {
     pub account_list: Vec<Vec<String>>,
     pub exchanges: Vec<String>,
     pub range_time: Vec<String>,
+    pub count_range_time: Vec<String>,
     pub export_path: String,
     pub export_name: String,
 }
@@ -83,6 +84,7 @@ impl BalanceConfigInfo {
             account_list: vec![],
             exchanges: vec![],
             range_time: vec![],
+            count_range_time: vec![],
             export_path: "".to_string(),
             export_name: "".to_string(),
         }