Selaa lähdekoodia

接入html数据绘制(未完成)

JiahengHe 10 kuukautta sitten
vanhempi
commit
09f2b98688
2 muutettua tiedostoa jossa 557 lisäystä ja 60 poistoa
  1. 123 60
      src/strategy.rs
  2. 434 0
      src/utils.rs

+ 123 - 60
src/strategy.rs

@@ -1,5 +1,7 @@
+use futures_channel::mpsc::UnboundedSender;
 use std::cmp::{max, min};
 use std::collections::HashMap;
+use std::collections::VecDeque;
 use std::ops::{Div, Mul};
 use std::str::FromStr;
 use chrono::Utc;
@@ -103,6 +105,10 @@ pub struct Strategy {
     // 速度限制,至少0.5秒才取消订单
     pub prev_place_order_timestamp: i64,                            // 上次挂单的时间
     pub min_cancel_interval_mills: i64,                             // 至少要挂这么久才允许撤销
+
+
+    pub prev_insert_time: Decimal,
+    pub debug_sender: UnboundedSender<Vec<Decimal>>,
 }
 
 impl Strategy {
@@ -111,6 +117,49 @@ impl Strategy {
             error!("参考盘口数不等于参考品种数,退出,请检查配置!");
             panic!("参考盘口数不等于参考品种数,退出,请检查配置!");
         }
+
+        // 创建数据通道
+        // 创建一个无界通道
+        let (tx, mut rx) = futures_channel::mpsc::unbounded::<Vec<Decimal>>();
+
+        tokio::spawn(async move {
+            let len = 16usize;
+            let mut prev_save_time = Decimal::from(Utc::now().timestamp_millis());
+            let mut debugs: Vec<VecDeque<Option<Decimal>>> = vec![VecDeque::new(); len];
+
+            while let Some(value) = rx.next().await {
+                // 数据填充到对应位置
+                for i in 0..len {
+                    if value[i] == Decimal::from_str("14142135623730951").unwrap() {
+                        debugs[i].push_back(None);
+                    } else {
+                        debugs[i].push_back(Some(value[i]));
+                    }
+                }
+
+                // 长度限制
+                if debugs[0].len() > 500_000 {
+                    for i in 0..len {
+                        debugs[i].pop_front(); // 从前面移除元素
+                    }
+                }
+
+                let now = Decimal::from(Utc::now().timestamp_millis());
+                if now - prev_save_time < Decimal::from_str("60000").unwrap() {
+                    continue;
+                }
+
+                let debugs_clone = debugs.clone();
+                let temp_html_str = tokio::task::spawn_blocking(move || {
+                    utils::build_html_file(&debugs_clone)
+                }).await.unwrap();
+
+                utils::write_to_file(&temp_html_str, "./db/db.html".to_string()).await;
+                prev_save_time = Decimal::from(Utc::now().timestamp_millis());
+            }
+        });
+
+
         // strategy的初始化,里面已经有一些参数初始化了
         let mut strategy = Self {
             _print_time: 0,
@@ -195,6 +244,8 @@ impl Strategy {
             grid: Decimal::from(params.grid),
             prev_place_order_timestamp: 0,
             min_cancel_interval_mills: 500,
+            prev_insert_time: Default::default(),
+            debug_sender: tx,
         };
 
         // 交易名字
@@ -1267,70 +1318,82 @@ impl Strategy {
         if command.limits_open.len() != 0 {
             self.prev_place_order_timestamp = Utc::now().timestamp_millis();
         }
+        command
+    }
+
+
+    async fn processor(&mut self) {
+        self.update_t_diff();
+        self.update_sigma_square();
+        self.update_gamma();
+        self.update_kappa();
+        self.update_delta();
+        self.update_optimal_ask_and_bid();
+
+        self.check_ready();
+        if !self.is_ready {
+            return;
+        }
 
-        // if command.limits_open.len() != 0 || command.limits_close.len() != 0 {
-        //     let name = self.params.account_name.clone();
-        //     // 参考卖价
-        //     let ref_ap = self.ref_ap;
-        //     // 参考买价
-        //     let ref_bp = self.ref_bp;
-        //     let limits_open = command.limits_open.clone();
-        //     let limits_close = command.limits_close.clone();
-        //     spawn(async move {
-        //         let time = chrono::Utc::now().timestamp_millis();
-        //         let param_list = paras_limit_command(name.clone(), time.clone(), ref_ap.clone(), ref_bp.clone(), limits_open, limits_close);
-        //         let param_json_obj = serde_json::to_string(&param_list).unwrap();
-        //         market_warehouse_request(param_json_obj).await;
-        //     });
+        // let mut smm = Decimal::ZERO;
+        // if !self.depth_vec[1].time.is_zero() {
+        //     let sma = self.depth_vec[1].asks[0].price;
+        //     let smb = self.depth_vec[1].bids[0].price;
+        //     smm = (sma + smb) / Decimal::TWO;
         // }
 
-        command
+
+        // let cci_arc = self.cci_arc.clone();
+        let now = Decimal::from_i64(Utc::now().timestamp_millis()).unwrap();
+        let mid_price = self.mid_price;
+        let ask_price = self.ask_price;
+        let bid_price = self.bid_price;
+        let last_price = self.last_price;
+
+        let spread = self.mid_price;
+        let spread_max = self.optimal_ask_price;
+        let spread_min = self.optimal_bid_price;
+        // let spread = self.price_times_avg;
+        // let spread_max = self.fair_price_vec[1] / self.fair_price_vec[0];
+        // let spread_min = self.fair_price / self.mid_price;
+
+        let optimal_ask_price = self.optimal_ask_price;
+        let optimal_bid_price = self.optimal_bid_price;
+
+        let inventory = self.inventory;
+        let sigma_square = if self.is_regressed { Decimal::ONE } else { Decimal::ZERO };
+        let gamma = now - self.last_update_time;
+        let kappa = self.fair_price / self.mid_price;
+
+        let flow_ratio = Decimal::ZERO;
+        let ref_price = self.fair_price;
+
+        let need_append = now - self.prev_insert_time > Decimal::ONE_HUNDRED;
+        if !need_append {
+            return;
+        }
+
+        self.debug_sender.unbounded_send(vec![
+            now,
+            mid_price,
+            ask_price,
+            bid_price,
+            last_price,
+            spread,
+            spread_max,
+            spread_min,
+            optimal_ask_price,
+            optimal_bid_price,
+            inventory,
+            sigma_square,
+            gamma,
+            kappa,
+            flow_ratio,
+            ref_price
+        ]).unwrap();
+
+        self.prev_insert_time = Decimal::from(Utc::now().timestamp_millis())
     }
 }
 
-// async fn market_warehouse_request(body_params: String) {
-//     /****请求接口与 地址*/
-//     let url = "http://as.skyfffire.com:8848/basic/saveDealRecords";
-//
-//     let client = Client::new();
-//     let req = client.post(url).header("auth", "43626546liangjiang")
-//         .header("Content-Type", "application/json").body(body_params.clone());
-//
-//     match req.send().await {
-//         Ok(_) => {}
-//         Err(_) => {}
-//     };
-//     // if !response.status().is_success()  {
-//     //     error!("行情数据------仓库挂单数据存储失败--------!{}", response.status());
-//     //     error!(body_params);
-//     // }
-// }
-//
-// fn paras_limit_command (robot_name: String, time: i64, ref_ap: Decimal, ref_bp: Decimal, limits_open: HashMap<String, Vec<String>>, limits_close: HashMap<String, Vec<String>>) -> Vec<DealRecord>{
-//     let mut limits = HashMap::new();
-//     limits.extend(limits_open);
-//     limits.extend(limits_close);
-//     let mut list: Vec<DealRecord> = Vec::with_capacity(limits.len());
-//     for item in limits.keys() {
-//         let item_clone = item.clone();
-//         let value = limits[&item_clone].clone();
-//         let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
-//         let side = value.get(1).unwrap();
-//         let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
-//         let mut ref_price = ref_ap;
-//         if "kd" == side {
-//             ref_price = ref_bp;
-//         }
-//         let deal_recode = DealRecord {
-//             refPrice: ref_price.to_string(),
-//             regPrice: price.to_string(),
-//             num: amount.to_string(),
-//             triggerTime: time,
-//             robotName: robot_name.clone(),
-//             side: side.to_string(),
-//         };
-//         list.push(deal_recode);
-//     }
-//     return list;
-// }
 

+ 434 - 0
src/utils.rs

@@ -1,9 +1,14 @@
+use tokio::fs;
 use std::ops::{Div, Mul};
+use std::path::Path;
 use chrono::Utc;
 use rand::Rng;
 use rust_decimal::Decimal;
 use tracing::error;
 use crate::public_params;
+use tokio::fs::File;
+use tokio::io::AsyncWriteExt;
+use std::collections::VecDeque;
 
 // 生成订单的id,可以根据交易所名字来
 pub fn generate_client_id(exchange_name_some: Option<String>) -> String {
@@ -117,6 +122,435 @@ pub fn get_limit_order_requests_num_per_second(exchange: String) -> i64 {
     }
 }
 
+pub async fn write_to_file(json_data: &String, file_path: String) {
+    // 尝试创建文件路径
+    if let Err(e) = fs::create_dir_all(
+        // 获取文件目录路径
+        Path::new(&file_path)
+            .parent() // 获取父目录(即文件路径除去文件名后的部分)
+            .unwrap_or_else(|| Path::new("")), // 如果没有父目录,使用当前目录
+    )
+        .await
+    {
+        // 如果创建路径失败,打印错误日志
+        error!("创建目录错误: {:?}", e);
+        return; // 结束任务
+    }
+
+    // 异步地执行文件写入操作
+    if let Err(e) = async {
+        let mut file = File::create(&file_path).await?;
+        file.write_all(json_data.as_bytes()).await?;
+        Result::<(), std::io::Error>::Ok(())
+    }
+        .await
+    {
+        // 如果发生错误,只打印错误日志
+        error!("json db写入错误: {:?}", e);
+    }
+}
+
+pub fn build_html_file(data_c: &Vec<VecDeque<Option<Decimal>>>) -> String {
+    let temp_json_str = serde_json::to_string(&data_c).unwrap();
+
+    let str1 = r##"
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Document</title>
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+    <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;
+      }
+      #echarts {
+        margin: 50px auto 0;
+        width: calc(100vw - 100px);
+        height: 1030px;
+      }
+      .page-group-wp {
+        padding: 20px 40px;
+        display: flex;
+        justify-content: space-between;
+      }
+      .page-button {
+        cursor: pointer;
+        border: 1px solid #16b777;
+        color: #16b777;
+        padding: 2px 10px;
+        user-select: none;
+      }
+      .disabled {
+        border: 1px solid #ccc;
+        color: #ccc;
+        cursor: not-allowed;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="page-group-wp">
+      <div class="page-button upper-btn">上一页</div>
+      <div class="show-page">第1/1页</div>
+      <div class="page-button next-btn">下一页</div>
+    </div>
+    <div id="echarts"></div>
+  </body>
+  <script>
+    let initData = "##;
+
+    let str2 = r##";
+    let init_echarts_data = (data) => {
+      const xData = data[0].map((item) => dayjs(item * 1).format("MM-DD HH:mm:ss:SSS"));
+      let inventory = 0;
+      let inventoryList = [];
+
+      data[10].map((item, index) => {
+        if (inventory != item) {
+          inventory = item;
+          let color = "";
+          if(item > 0) color = "#F2495E"
+          if(item < 0) color = "#13BF86"
+          inventoryList.push({ name: "inventory",itemStyle:{color: color}, value: item, xAxis: index, yAxis: data[1][index] });
+        }
+      });
+      return {
+        dataZoom: [
+          {
+            type: "inside",
+            xAxisIndex: [0, 1, 2, 3, 4],
+            start: 0,
+            end: 100,
+          },
+          {
+            xAxisIndex: [0, 1, 2, 3, 4],
+            start: 0,
+            end: 100,
+          },
+        ],
+        tooltip: {
+          trigger: "axis",
+          formatter: (value) => {
+            return `时间:${value[0].name}<br />${value.map((item) => `${item.marker}${item.seriesName}:${item.value}`).join("<br/>")}`;
+          },
+          axisPointer: {
+            animation: false,
+          },
+        },
+        toolbox: {
+          feature: {
+            dataZoom: {},
+            brush: {
+              type: ["rect", "clear"],
+            },
+          },
+        },
+        legend: [
+          {
+            data: ["mid_price", "ask_price", "bid_price", "optimal_ask_price", "optimal_bid_price", "fair_price"],
+            top: "230px",
+          },
+          {
+            data: ["mp", "ap", "bp"],
+            top: "480px",
+          },
+          {
+            data: ["sigma_square"],
+            top: "630px",
+          },
+          {
+            data: ["delay"],
+            top: "780px",
+          },
+          {
+            data: ["kappa"],
+            top: "930px",
+          },
+        ],
+        grid: [
+          {
+            top: "50px",
+            left: "60px",
+            right: "60px",
+            height: "150px", // 主图高度
+          },
+          {
+            top: "300px",
+            left: "60px",
+            right: "60px",
+            height: "150px",
+          },
+          {
+            top: "550px",
+            left: "60px",
+            right: "60px",
+            height: "50px",
+          },
+          {
+            top: "700px",
+            left: "60px",
+            right: "60px",
+            height: "50px",
+          },
+          {
+            top: "850px",
+            left: "60px",
+            right: "60px",
+            height: "50px",
+          },
+        ],
+        xAxis: [
+          {
+            type: "category",
+            data: xData,
+          },
+          {
+            gridIndex: 1, // 第1个网格的 x 轴
+            type: "category",
+            data: xData,
+          },
+          {
+            gridIndex: 2, // 第2个网格的 x 轴
+            type: "category",
+            data: xData,
+          },
+          {
+            gridIndex: 3, // 第3个网格的 x 轴
+            type: "category",
+            data: xData,
+          },
+          {
+            gridIndex: 4, // 第4个网格的 x 轴
+            type: "category",
+            data: xData,
+          },
+        ],
+        yAxis: [
+          {
+            type: "value",
+            min: "dataMin",
+          },
+          {
+            gridIndex: 1, // 第1个网格的 y 轴
+            type: "value",
+            min: "dataMin",
+          },
+          {
+            gridIndex: 2, // 第2个网格的 y 轴
+            type: "value",
+            min: "dataMin",
+          },
+          {
+            gridIndex: 3, // 第3个网格的 y 轴
+            type: "value",
+            min: "dataMin",
+          },
+          {
+            gridIndex: 4, // 第4个网格的 y 轴
+            type: "value",
+            min: "dataMin",
+          },
+        ],
+        series: [
+          {
+            name: "mid_price",
+            type: "line",
+            showSymbol: false,
+            data: data[1],
+            markPoint: {
+              symbolSize: 20,
+              data: inventoryList,
+            },
+          },
+          {
+            name: "ask_price",
+            type: "line",
+            showSymbol: false,
+            // data: data[2],
+            data: [],
+          },
+          {
+            name: "bid_price",
+            type: "line",
+            showSymbol: false,
+            // data: data[3],
+            data: [],
+          },
+          {
+            name: "optimal_ask_price",
+            type: "line",
+            showSymbol: false,
+            // data: data[8],
+            data: [],
+            lineStyle: {
+              type: "dashed",
+            },
+          },
+          {
+            name: "optimal_bid_price",
+            type: "line",
+            showSymbol: false,
+            // data: data[9],
+            data: [],
+            lineStyle: {
+              type: "dashed",
+            },
+          },
+          {
+            name: "fair_price",
+            type: "line",
+            showSymbol: false,
+            data: data[15],
+          },
+
+          {
+            name: "mp",
+            type: "line",
+            xAxisIndex: 1,
+            yAxisIndex: 1,
+            showSymbol: false,
+            data: data[5],
+          },
+          {
+            name: "ap",
+            type: "line",
+            xAxisIndex: 1,
+            yAxisIndex: 1,
+            showSymbol: false,
+            data:  data[6],
+          },
+          {
+            name: "bp",
+            type: "line",
+            xAxisIndex: 1,
+            yAxisIndex: 1,
+            showSymbol: false,
+            data:  data[7],
+          },
+
+          {
+            name: "sigma_square",
+            type: "line",
+            xAxisIndex: 2,
+            yAxisIndex: 2,
+            showSymbol: false,
+            data:  data[11],
+          },
+
+          {
+            name: "delay",
+            type: "line",
+            xAxisIndex: 3,
+            yAxisIndex: 3,
+            showSymbol: false,
+            data:  data[12],
+          },
+
+          {
+            name: "kappa",
+            type: "line",
+            xAxisIndex: 4,
+            yAxisIndex: 4,
+            showSymbol: false,
+            data:  data[13],
+          },
+        ],
+      };
+    };
+
+    let update_echarts_data = (data) => {
+      const xData = data[0].map((item) => dayjs(item * 1).format("MM-DD HH:mm:ss:SSS"));
+      let inventory = 0;
+      let inventoryList = [];
+
+      data[10].map((item, index) => {
+        if (inventory != item) {
+          inventory = item;
+          let color = "";
+          if(item > 0) color = "#F2495E"
+          if(item < 0) color = "#13BF86"
+          inventoryList.push({ name: "inventory",itemStyle:{color: color}, value: item, xAxis: index, yAxis: data[1][index] });
+        }
+      });
+      return {
+        xAxis: [{ data: xData }, { data: xData }, { data: xData }, { data: xData }, { data: xData }],
+        series: [
+          {
+            data: data[1],
+            markPoint: {
+              data: inventoryList,
+            },
+          },
+          // { data: data[2] },
+          // { data: data[3] },
+          // { data: data[8] },
+          // { data: data[9] },
+          { data: [] },
+          { data: [] },
+          { data: [] },
+          { data: [] },
+          { data: data[15] },
+          { data: data[5] },
+          { data: data[6] },
+          { data: data[7] },
+          { data: data[11] },
+          { data: data[12] },
+          { data: data[13] },
+        ],
+      };
+    };
+
+    let page = 1;
+    let maxPage = initData[0].length % 100000 > 0 ? Math.ceil(initData[0].length / 100000) : initData[0].length / 100000 || 1;
+    let showData = initData.map((item) => item.slice((page - 1) * 100000, page * 100000 - 1));
+    let chartDom = document.getElementById("echarts");
+    let myChart = echarts.init(chartDom);
+    let initOption = init_echarts_data(showData);
+    initOption && myChart.setOption(initOption);
+
+    let updata_page = () => {
+      $(".show-page").eq(0).html(`第${page}/${maxPage}页`);
+      $(".next-btn").eq(0).removeClass("disabled");
+      $(".upper-btn").eq(0).removeClass("disabled");
+      if (page >= maxPage) $(".next-btn").eq(0).addClass("disabled");
+      if (page <= 1) $(".upper-btn").eq(0).addClass("disabled");
+    };
+    updata_page();
+    let update_echarts = () => {
+      showData = initData.map((item) => item.slice((page - 1) * 100000, page * 100000 + 99999));
+      let option = update_echarts_data(showData);
+      myChart.setOption(option, false, true);
+    };
+
+    $(".next-btn")
+      .eq(0)
+      .click(() => {
+        if (page < maxPage) {
+          page += 1;
+          update_echarts();
+          updata_page();
+        }
+      });
+    $(".upper-btn")
+      .eq(0)
+      .click(() => {
+        if (page > 1) {
+          page -= 1;
+          update_echarts();
+          updata_page();
+        }
+      });
+  </script>
+</html>"##;
+
+    format!("{}{}{}", str1, temp_json_str, str2)
+}
+
+
 #[cfg(test)]
 mod tests {
     use chrono::Utc;