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

添加导出余额柱状图统计

gepangpang пре 1 година
родитељ
комит
e51c2caaeb

+ 8 - 0
config_columnar.toml.sample

@@ -0,0 +1,8 @@
+#是否执行导出
+is_export = true
+#range_time = ["2024-2-8 10:00:00", "2024-2-9 10:00:00"]
+range_time = ["2024-2-5 10:00:00", "2024-03-6 10:00:00"]
+# 导出路径(不填则为默认路径"./")
+export_path = ""
+# 导出文件名字(不填则为默认名字"export")
+export_name = ""

+ 2 - 4
src/export_balance.rs

@@ -2,7 +2,7 @@ use std::collections::{BTreeMap};
 use std::str::FromStr;
 use chrono::NaiveDateTime;
 use rust_decimal::Decimal;
-use rust_decimal::prelude::ToPrimitive;
+// use rust_decimal::prelude::ToPrimitive;
 use rust_decimal_macros::dec;
 use tracing::{error, info};
 use crate::{export_template};
@@ -222,7 +222,7 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
     };
 }
 
-pub fn statistic_balance(balance_info: Vec<Vec<String>>, count_start_time: i64, end_start_time: i64, config: BalanceConfigInfo) -> String {
+pub fn statistic_balance(balance_info: Vec<Vec<String>>, count_start_time: i64, end_start_time: i64, _config: BalanceConfigInfo) -> String {
     let mut max_withdrawal = dec!(0);
     let mut max_total_withdrawal = dec!(0);
     let mut total_income = dec!(0);
@@ -293,8 +293,6 @@ pub fn supply_balance(source_balance_info: BTreeMap<String, Vec<Vec<String>>>, t
                     break;
                 }
             }
-            println!("{:?}", new_info);
-            ;
             match new_info {
                 None => {
                     let balance_info_filter: Vec<Vec<String>> = balance_info.iter().filter(|item| {

+ 39 - 0
src/export_columnar.rs

@@ -0,0 +1,39 @@
+use chrono::NaiveDateTime;
+use crate::export_template::template_columnar;
+use crate::utils::utils;
+use crate::utils::utils::{ColumnarConfigInfo};
+
+pub async fn export_columnar(config_info: ColumnarConfigInfo) {
+    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 history_balance: serde_json::Value = utils::get_json_file("./config_history_balance.json");
+
+    let mut time_slicer: Vec<i64> = vec![start_time];
+    let mut time_float = start_time;
+    loop {
+        time_float = time_float + 60 * 60 * 24;
+        time_slicer.push(time_float);
+        if time_float >= end_time { break; }
+    }
+    let balance_list = history_balance["balance_list"]["total_balance"].as_array().clone().unwrap();
+    let mut time_list = time_slicer.clone();
+    let mut over_list = vec![];
+    let mut save_value: Vec<String> = vec![];
+    for (index, item) in balance_list.iter().enumerate() {
+        let time: i64 = item[1].as_str().unwrap().parse().unwrap();
+        if time_list[0] < time {
+            save_value[1] = time_list[0].to_string();
+            over_list.push(save_value.clone());
+            time_list.remove(0);
+        }
+        save_value = item.as_array().unwrap().iter().map(|value| value.as_str().unwrap().to_string()).collect();
+        if index == balance_list.len() - 1 {
+            if time <= time_list[0] {
+                save_value[1] = time_list[0].to_string();
+                over_list.push(save_value.clone());
+                time_list.remove(0);
+            }
+        }
+    }
+    template_columnar::export_html(config_info, over_list);
+}

+ 2 - 1
src/export_template/mod.rs

@@ -1,3 +1,4 @@
 pub mod template_ticker;
 pub mod template_balance;
-pub mod template_analyze;
+pub mod template_analyze;
+pub mod template_columnar;

+ 1 - 1
src/export_template/template_balance.rs

@@ -5,7 +5,7 @@ use std::str::FromStr;
 use chrono::NaiveDateTime;
 use handlebars::Handlebars;
 use rust_decimal::Decimal;
-use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+// use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
 use rust_decimal_macros::dec;
 use serde_json::json;
 use tracing::info;

+ 125 - 0
src/export_template/template_columnar.rs

@@ -0,0 +1,125 @@
+use std::fs::File;
+use std::io::Write;
+// use std::str::FromStr;
+// use chrono::NaiveDateTime;
+use handlebars::Handlebars;
+// use rust_decimal::Decimal;
+// use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+// use rust_decimal_macros::dec;
+// use serde_json::json;
+use tracing::info;
+use crate::utils::utils::{ColumnarConfigInfo};
+
+
+pub fn export_html(config: ColumnarConfigInfo, data_list: Vec<Vec<String>>) {
+    info!("正在生成网页,请稍后!");
+    let export_path = if config.export_path == "" { "./" } else { config.export_path.as_str() };
+    let export_name = if config.export_name == "" { "export_columnar" } else { config.export_name.as_str() };
+    let path = format!("{}/{}.html", export_path, export_name).replace("//", "/");
+    // 创建 Handlebars 实例
+    let mut handlebars = Handlebars::new();
+
+    let x_values: Vec<String> = data_list.clone().iter().map(|item| item[1].clone()).collect();
+    let y_values: Vec<String> = data_list.clone().iter().map(|item| item[4].clone()).collect();
+
+    let data = serde_json::json!({
+        "chart_title": format!("余额统计"),
+        "x_values": x_values,
+        "y_values": y_values,
+    });
+    // HTML 模板
+    let template = r#"
+        <!DOCTYPE html>
+        <html>
+        <head>
+            <meta charset="UTF-8">
+            <title>{{chart_title}}</title>
+            <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
+            <script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/dayjs.min.js"></script>
+
+            <style>
+                * {
+                    margin: 0;
+                    padding: 0;
+                }
+                #main {
+                    margin: 50px auto 0;
+                    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'};
+            var chartDom = document.getElementById('main');
+            var myChart = echarts.init(chartDom);
+            var option;
+
+            option = {
+              title: {
+                text: '{{chart_title}}'
+              },
+              tooltip: {
+                trigger: 'axis',
+                formatter: function (value) {
+                  let time = dayjs(value[0].name * 1000).format('YYYY-MM-DD');
+                  let price = value[0].value;
+                  return `时间:${time}<br/>价格:${price}`
+                },
+              },
+              xAxis: {
+                type: 'category',
+                data: {{x_values}},
+                axisTick: {
+                    alignWithLabel: true
+                },
+                axisLabel: {
+                    formatter: function (value) {
+                        return dayjs(value * 1000).format('YYYY-MM-DD');
+                    }
+                }
+              },
+              yAxis: {
+                type: 'value',
+              },
+              series: [
+                {
+                  name: '余额',
+                  type: 'bar',
+                  label: {
+                    show: true,
+                    position: 'inside',
+                    rotate: 90
+                  },
+                  data: {{y_values}}
+                }
+              ]
+            };
+
+            option && myChart.setOption(option);
+            </script>
+        </html>
+    "#;
+
+    // 编译模板
+    handlebars
+        .register_template_string("page", template)
+        .expect("编译模版失败!");
+
+    // 渲染模板
+    let output = handlebars
+        .render("page", &data)
+        .expect("渲染模版失败!");
+
+    // 将 HTML 写入文件
+    let mut file = File::create(&path).expect("创建文件失败!");
+    file.write_all(output.as_bytes()).expect("写入文件到本地失败!");
+    info!("Columnar信息网页生成成功!路径:{:?}\n\n", path);
+}

+ 12 - 3
src/main.rs

@@ -2,7 +2,7 @@ use tokio::spawn;
 use tokio::task::JoinHandle;
 use tracing::info;
 use crate::utils::logs;
-use crate::utils::utils::{AnalyzeConfigInfo, BalanceConfigInfo, TickerConfigInfo};
+use crate::utils::utils::{AnalyzeConfigInfo, BalanceConfigInfo, ColumnarConfigInfo, TickerConfigInfo};
 
 pub mod swap_binance;
 pub mod swap_gate;
@@ -17,11 +17,13 @@ pub mod export_balance;
 pub mod new_export_balance;
 pub mod export_ticker;
 pub mod export_analyze;
+pub mod export_columnar;
 
 struct ConfigList {
     ticker_info: TickerConfigInfo,
     balance_info: BalanceConfigInfo,
-    analyze_info: AnalyzeConfigInfo
+    columnar_info: ColumnarConfigInfo,
+    analyze_info: AnalyzeConfigInfo,
 }
 
 #[tokio::main]
@@ -29,11 +31,13 @@ async fn main() {
     logs::init_log_with_info();
     let ticker_config = utils::utils::get_ticker_config_info("./config_ticker.toml");
     let balance_config = utils::utils::get_balance_config_info("./config_balance.toml");
+    let columnar_config = utils::utils::get_columnar_config_info("./config_columnar.toml");
     let analyze_config = utils::utils::get_analyze_config_info("./config_analyze.toml");
     let config_obj = ConfigList {
         ticker_info: ticker_config,
         balance_info: balance_config,
-        analyze_info: analyze_config
+        columnar_info: columnar_config,
+        analyze_info: analyze_config,
     };
 
     if config_obj.ticker_info.is_export {
@@ -52,6 +56,11 @@ async fn main() {
         }
         new_export_balance::export_balance(config_clone).await;
     }
+    if config_obj.columnar_info.is_export {
+        info!("----------正在导出Columnar信息----------");
+        let config_clone = config_obj.columnar_info.clone();
+        export_columnar::export_columnar(config_clone).await;
+    }
 
     if config_obj.analyze_info.is_export {
         info!("----------正在导出Balance信息----------");

+ 6 - 9
src/new_export_balance.rs

@@ -1,6 +1,4 @@
 use std::collections::BTreeMap;
-use std::fs::File;
-use std::io::BufReader;
 use std::str::FromStr;
 use chrono::NaiveDateTime;
 use rust_decimal::Decimal;
@@ -9,7 +7,6 @@ use serde_json::json;
 use tracing::{error, info};
 use crate::export_template;
 use crate::http::request::Response;
-use crate::swap_bybit::bybit_swap_rest_utils::BybitSwapRest;
 use crate::swap_gate::gate_swap_rest_utils::GateSwapRest;
 use crate::utils::utils;
 use crate::utils::utils::BalanceConfigInfo;
@@ -32,9 +29,9 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
 
     let mut count_balance_info = vec![];
     let mut count_time_list = vec![];
-    let mut history_balance: serde_json::Value = utils::get_json_file("./config_history_balance.json");
+    let history_balance: serde_json::Value = utils::get_json_file("./config_history_balance.json");
     let history_start_time = history_balance["start_time"].as_i64().unwrap_or(0);
-    let mut history_end_time = history_balance["end_time"].as_i64().unwrap_or(0);
+    let history_end_time = history_balance["end_time"].as_i64().unwrap_or(0);
     if history_end_time < end_time {
         for exchange in config_info.exchanges.clone() {
             let exchange_up = exchange.to_uppercase();
@@ -194,7 +191,7 @@ pub async fn export_balance(config_info: BalanceConfigInfo) {
     let new_data = save_balance(history_balance.clone(), count_balance_info.clone(), save_start_time, save_end_time, history_end_time);
     let mut over_balance_info = vec![];
     let mut last_count_time_list = vec![];
-    for (key, value) in new_data.clone() {
+    for (_key, value) in new_data.clone() {
         last_count_time_list = vec![];
         for item in value {
             over_balance_info.push(item.clone());
@@ -244,7 +241,7 @@ pub fn save_balance(mut history_balance: serde_json::Value, new_balance: Vec<Vec
         }
     }
     for (key, value) in new_data.clone() {
-        let mut value_clone = value.clone();
+        let value_clone = value.clone();
         // if history_balance_list.contains_key(&key.clone()) { value_clone.remove(0); };
         history_balance_list.entry(key.clone())
             .and_modify(|v| v.extend(value_clone.clone()))
@@ -257,7 +254,7 @@ pub fn save_balance(mut history_balance: serde_json::Value, new_balance: Vec<Vec
     history_balance_list
 }
 
-pub fn statistic_balance(balance_info: BTreeMap<String, Vec<Vec<String>>>, count_start_time: i64, end_start_time: i64, config: BalanceConfigInfo) -> String {
+pub fn statistic_balance(balance_info: BTreeMap<String, Vec<Vec<String>>>, count_start_time: i64, end_start_time: i64, _config: BalanceConfigInfo) -> String {
     let mut max_withdrawal = dec!(0);
     let mut max_total_withdrawal = dec!(0);
     let mut total_income = dec!(0);
@@ -291,7 +288,7 @@ pub fn statistic_balance(balance_info: BTreeMap<String, Vec<Vec<String>>>, count
             let mut present = Decimal::from_str(&balance[0][4]).unwrap();
             let mut past = dec!(0);
             let mut earnings = dec!(0);
-            for (index, info) in balance.iter().enumerate() {
+            for (_index, info) in balance.iter().enumerate() {
                 if info[5] == "transfer" {
                     total_income += earnings;
                     present = Decimal::from_str(&info[4]).unwrap();

+ 1 - 1
src/swap_gate/gate_swap_rest_utils.rs

@@ -96,7 +96,7 @@ impl GateSwapRest {
         ).await;
         data
     }
-    pub async fn fill_account_book(&mut self, settle: String, start_time: i64, end_time: i64) -> Response {
+    pub async fn fill_account_book(&mut self, settle: String, _start_time: i64, end_time: i64) -> Response {
         let params = serde_json::json!({
             "limit":1,
             "from":end_time-60*60*24*30,

+ 36 - 0
src/utils/utils.rs

@@ -114,6 +114,42 @@ pub fn get_balance_config_info(file_path: &str) -> BalanceConfigInfo {
     result
 }
 
+#[derive(Debug, Clone, Deserialize)]
+pub struct ColumnarConfigInfo {
+    pub is_export: bool,
+    pub range_time: Vec<String>,
+    pub export_path: String,
+    pub export_name: String,
+}
+
+impl ColumnarConfigInfo {
+    fn new() -> ColumnarConfigInfo {
+        ColumnarConfigInfo {
+            is_export: false,
+            range_time: vec![],
+            export_path: "".to_string(),
+            export_name: "".to_string(),
+        }
+    }
+}
+
+// 获取文件内容
+pub fn get_columnar_config_info(file_path: &str) -> ColumnarConfigInfo {
+    let file = File::open(file_path);
+    let mut contents = String::new();
+    let result = match file {
+        Ok(mut value) => {
+            value.read_to_string(&mut contents).unwrap_or_default();
+            from_str(&contents).unwrap_or(ColumnarConfigInfo::new())
+        }
+        Err(_) => {
+            error!("没有获取到配置文件!");
+            ColumnarConfigInfo::new()
+        }
+    };
+    result
+}
+
 #[derive(Debug, Clone, Deserialize)]
 pub struct AnalyzeConfigInfo {
     pub is_export: bool,