Переглянути джерело

新增实时币对排行页面

DESKTOP-NE65RNK\Citrus_limon 1 рік тому
батько
коміт
ee85de3b16

+ 6 - 0
src/api/index.ts

@@ -455,6 +455,12 @@ export const get_symbols_rank = (params: any, callback: any) => {
     if (data) callback && callback(data);
   });
 };
+// 情报中心-币对排行列表(实时)
+export const get_symbols_rank_real = (params: any, callback: any) => {
+  return http.request("/rk/get_symbols_rank_real", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
 // 情报中心-币对排行交易所
 export const get_exchange_rank = (params: any, callback: any) => {
   return http.request("/rk/get_exchanges", "get", params).then((data) => {

+ 6 - 0
src/router/routes.ts

@@ -82,6 +82,12 @@ const routes: Array<RouteRecordRaw> = [
         component: () => import("@/views/indicator/symbol_rank/index.vue"),
         meta: { title: "币对排行", keepAlive: true },
       },
+      {
+        path: "/indicator/symbol_rank_real",
+        name: "IndicatorSymbolRankReal",
+        component: () => import("@/views/indicator/symbol_rank_real/index.vue"),
+        meta: { title: "币对排行(实时)", keepAlive: true },
+      },
       {
         path: "/indicator/blacklists",
         name: "IndicatorBlacklists",

+ 3 - 472
src/views/bot/manage/detail.vue

@@ -28,14 +28,14 @@
         <div class="profit-chart" ref="balanceChartRef"></div>
       </template>
     </lay-card>
-    <lay-card class="custom-card">
+    <!-- <lay-card class="custom-card">
       <template v-slot:title>
         <span class="card-title">净值图</span>
       </template>
       <template v-slot:body>
         <div class="predictor-chart" ref="predictorChartRef" @mouseleave="startPredictorInterval" @mouseenter="stopPredictorInterval"></div>
       </template>
-    </lay-card>
+    </lay-card> -->
 
     <lay-card v-if="apiList?.includes('/robot/getRobotLog')" class="custom-card">
       <template v-slot:title>
@@ -57,9 +57,8 @@
 import { ref, reactive, onMounted, onUnmounted, shallowRef } from "vue";
 import { useRoute } from "vue-router";
 import * as echarts from "echarts";
-import dayjs from "dayjs";
 import LogText from "./components/LogText.vue";
-import { get_robot_detail, get_robot_logs, get_remaining_detail, get_predictor_state } from "@/api";
+import { get_robot_detail, get_robot_logs, get_remaining_detail } from "@/api";
 
 const ROBOT_STATUS: any = reactive({
   STOPPED: "已停止",
@@ -74,7 +73,6 @@ const ROBOT_STATUS: any = reactive({
 const apiList = ref(window.sessionStorage.getItem("_4L_API_LIST"));
 
 const balanceChartRef = ref();
-const predictorChartRef = ref();
 const logtextRef = ref();
 
 const route = useRoute();
@@ -98,11 +96,8 @@ const columns = ref([
 let logsList = ref<Array<Logs>>();
 let robotDetail = ref<any>({});
 let balanceList = ref([]);
-let predictorList = ref<any>([]);
 let balanceChart = shallowRef();
-let predictorChart = shallowRef();
 let timer = ref();
-let predictorInterval = ref();
 
 // 获取机器人详情
 const getRobotDetail = () => {
@@ -117,186 +112,6 @@ const getRobotDetail = () => {
     }
   });
 };
-
-const getPredictorState = () => {
-  const params = {};
-  pageConfig.loading = true;
-  get_predictor_state(params, (data: any) => {
-    pageConfig.loading = false;
-    console.log(data);
-    if (!data) return;
-    predictorList.value = data;
-    const xData = data.map((item: any) => dayjs(item.update_time * 1).format("MM-DD HH:mm:ss:SSS"));
-
-    !predictorChart.value
-      ? initPredictorChart(predictorList.value)
-      : predictorChart.value.setOption({
-          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,
-            },
-            {
-              gridIndex: 5, // 第5个网格的 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",
-            },
-            {
-              gridIndex: 5, // 第5个网格的 y 轴
-              type: "value",
-              min: "dataMin",
-            },
-          ],
-          series: [
-            {
-              name: "mid_price",
-              type: "line",
-              data: data.map((item: any) => item.mid_price),
-            },
-            {
-              name: "ask_price",
-              type: "line",
-              data: data.map((item: any) => item.ask_price),
-            },
-            {
-              name: "bid_price",
-              type: "line",
-              data: data.map((item: any) => item.bid_price),
-            },
-            {
-              name: "optimal_ask_price",
-              type: "line",
-              data: data.map((item: any) => item.optimal_ask_price),
-              lineStyle: {
-                type: "dashed",
-              },
-            },
-            {
-              name: "optimal_bid_price",
-              type: "line",
-              data: data.map((item: any) => item.optimal_bid_price),
-              lineStyle: {
-                type: "dashed",
-              },
-            },
-            {
-              name: "ref_price",
-              type: "line",
-              data: data.map((item: any) => item.ref_price),
-            },
-
-            {
-              name: "inventory",
-              type: "line",
-              xAxisIndex: 1,
-              yAxisIndex: 1,
-              data: data.map((item: any) => item.inventory),
-            },
-
-            {
-              name: "spread",
-              type: "line",
-              xAxisIndex: 2,
-              yAxisIndex: 2,
-              data: data.map((item: any) => item.spread),
-            },
-            {
-              name: "delta_plus",
-              type: "line",
-              xAxisIndex: 2,
-              yAxisIndex: 2,
-              data: data.map((item: any) => item.delta_plus),
-            },
-            {
-              name: "spread_max",
-              type: "line",
-              xAxisIndex: 2,
-              yAxisIndex: 2,
-              data: data.map((item: any) => item.spread_max),
-            },
-            {
-              name: "spread_min",
-              type: "line",
-              xAxisIndex: 2,
-              yAxisIndex: 2,
-              data: data.map((item: any) => item.spread_min),
-            },
-
-            {
-              name: "sigma_square",
-              type: "line",
-              xAxisIndex: 3,
-              yAxisIndex: 3,
-              data: data.map((item: any) => item.sigma_square),
-            },
-
-            {
-              name: "gamma",
-              type: "line",
-              xAxisIndex: 4,
-              yAxisIndex: 4,
-              data: data.map((item: any) => item.gamma),
-            },
-
-            {
-              name: "kappa",
-              type: "line",
-              xAxisIndex: 5,
-              yAxisIndex: 5,
-              data: data.map((item: any) => item.kappa),
-            },
-          ],
-        });
-  });
-};
-
-// 获取账户余额
 const getBalanceInfo = (id: number) => {
   const params = { id: id };
   pageConfig.loading = true;
@@ -418,271 +233,6 @@ const initBalanceChart = (data: any) => {
   balanceChart.value.setOption(balanceChartOption);
 };
 
-const initPredictorChart = (data: any) => {
-  if (predictorChart.value != null && !predictorChart.value.isDisposed()) echarts.dispose(predictorChart.value);
-  predictorChart.value = echarts.init(predictorChartRef.value);
-
-  window.removeEventListener("resize", () => predictorChart.value.resize());
-  window.addEventListener("resize", () => predictorChart.value.resize());
-  const xData = data.map((item: any) => dayjs(item.update_time * 1).format("MM-DD HH:mm:ss:SSS"));
-  const option = {
-    dataZoom: [
-      {
-        type: "inside",
-        xAxisIndex: [0, 1, 2, 3, 4, 5],
-        start: 0,
-        end: 100,
-      },
-      {
-        xAxisIndex: [0, 1, 2, 3, 4, 5],
-        start: 0,
-        end: 100,
-      },
-    ],
-    tooltip: {
-      trigger: "axis",
-      formatter: (value: any) => {
-        return `时间:${value[0].name}<br />${value.map((item: any) => `${item.marker}${item.seriesName}:${item.value}`).join("<br/>")}`;
-      },
-    },
-    toolbox: {
-      feature: {
-        dataZoom: {},
-        brush: {
-          type: ["rect", "clear"],
-        },
-      },
-    },
-    legend: [
-      {
-        data: ["mid_price", "ask_price", "bid_price", "optimal_ask_price", "optimal_bid_price", "ref_price"],
-        top: "230px",
-      },
-      {
-        data: ["inventory"],
-        top: "380px",
-      },
-      {
-        data: ["spread", "delta_plus", "spread_max", "spread_min"],
-        top: "630px",
-      },
-      {
-        data: ["sigma_square"],
-        top: "780px",
-      },
-      {
-        data: ["gamma"],
-        top: "930px",
-      },
-      {
-        data: ["kappa"],
-        top: "1080px",
-      },
-    ],
-
-    grid: [
-      {
-        top: "50px",
-        left: "60px",
-        right: "60px",
-        height: "150px", // 主图高度
-      },
-      {
-        top: "300px",
-        left: "60px",
-        right: "60px",
-        height: "50px",
-      },
-      {
-        top: "450px",
-        left: "60px",
-        right: "60px",
-        height: "150px",
-      },
-      {
-        top: "700px",
-        left: "60px",
-        right: "60px",
-        height: "50px",
-      },
-      {
-        top: "850px",
-        left: "60px",
-        right: "60px",
-        height: "50px",
-      },
-      {
-        top: "1000px",
-        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,
-      },
-      {
-        gridIndex: 5, // 第5个网格的 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",
-      },
-      {
-        gridIndex: 5, // 第5个网格的 y 轴
-        type: "value",
-        min: "dataMin",
-      },
-    ],
-    series: [
-      {
-        name: "mid_price",
-        type: "line",
-        data: data.map((item: any) => item.mid_price),
-      },
-      {
-        name: "ask_price",
-        type: "line",
-        data: data.map((item: any) => item.ask_price),
-      },
-      {
-        name: "bid_price",
-        type: "line",
-        data: data.map((item: any) => item.bid_price),
-      },
-      {
-        name: "optimal_ask_price",
-        type: "line",
-        data: data.map((item: any) => item.optimal_ask_price),
-        lineStyle: {
-          type: "dashed",
-        },
-      },
-      {
-        name: "optimal_bid_price",
-        type: "line",
-        data: data.map((item: any) => item.optimal_bid_price),
-        lineStyle: {
-          type: "dashed",
-        },
-      },
-      {
-        name: "ref_price",
-        type: "line",
-        data: data.map((item: any) => item.ref_price),
-      },
-
-      {
-        name: "inventory",
-        type: "line",
-        xAxisIndex: 1,
-        yAxisIndex: 1,
-        data: data.map((item: any) => item.inventory),
-      },
-
-      {
-        name: "spread",
-        type: "line",
-        xAxisIndex: 2,
-        yAxisIndex: 2,
-        data: data.map((item: any) => item.spread),
-      },
-      {
-        name: "delta_plus",
-        type: "line",
-        xAxisIndex: 2,
-        yAxisIndex: 2,
-        data: data.map((item: any) => item.delta_plus),
-      },
-      {
-        name: "spread_max",
-        type: "line",
-        xAxisIndex: 2,
-        yAxisIndex: 2,
-        data: data.map((item: any) => item.spread_max),
-      },
-      {
-        name: "spread_min",
-        type: "line",
-        xAxisIndex: 2,
-        yAxisIndex: 2,
-        data: data.map((item: any) => item.spread_min),
-      },
-
-      {
-        name: "sigma_square",
-        type: "line",
-        xAxisIndex: 3,
-        yAxisIndex: 3,
-        data: data.map((item: any) => item.sigma_square),
-      },
-
-      {
-        name: "gamma",
-        type: "line",
-        xAxisIndex: 4,
-        yAxisIndex: 4,
-        data: data.map((item: any) => item.gamma),
-      },
-
-      {
-        name: "kappa",
-        type: "line",
-        xAxisIndex: 5,
-        yAxisIndex: 5,
-        data: data.map((item: any) => item.kappa),
-      },
-    ],
-  };
-  predictorChart.value.setOption(option);
-};
-
 timer.value = setInterval(() => {
   getBalanceInfo(robotDetail.value.accId);
   getLogsInfo();
@@ -690,32 +240,13 @@ timer.value = setInterval(() => {
 
 getLogsInfo();
 
-const startPredictorInterval = () => {
-  console.log("startPredictorInterval");
-  if (!predictorInterval.value) {
-    predictorInterval.value = window.setInterval(() => {
-      getPredictorState();
-    }, 2000);
-  }
-};
-
-// 停止定时请求
-const stopPredictorInterval = () => {
-  if (predictorInterval.value) {
-    clearInterval(predictorInterval.value);
-    predictorInterval.value = null; // 释放定时器
-  }
-};
-
 onMounted(() => {
   getRobotDetail();
-  startPredictorInterval();
 });
 
 onUnmounted(() => {
   window.removeEventListener("resize", () => balanceChart.value.resize());
   clearInterval(timer.value);
-  stopPredictorInterval();
 });
 </script>
 

+ 226 - 0
src/views/indicator/symbol_rank_real/index.vue

@@ -0,0 +1,226 @@
+<template>
+  <lay-card class="custom-card">
+    <template v-slot:title>
+      <span class="card-title">币对分数排名</span>
+    </template>
+    <template v-slot:body>
+      <div class="custom-form-layout">
+        <lay-form class="form-wp" :model="pageParams" mode="inline" size="sm">
+          <lay-form-item label="盘口" prop="exchange">
+            <lay-select v-model="pageParams.exchange" :show-search="true">
+              <lay-select-option v-for="item of rkExchanges" :value="item" :label="item" />
+            </lay-select>
+          </lay-form-item>
+          <lay-form-item label="波动分数≥" prop="msvScore">
+            <lay-input v-model="pageParams.msvScore" />
+          </lay-form-item>
+          <lay-form-item label="交易量分数≥" prop="liquidityScore">
+            <lay-input v-model="pageParams.liquidityScore" />
+          </lay-form-item>
+          <lay-form-item label="交易频次分数≥" prop="frequencyScore">
+            <lay-input v-model="pageParams.frequencyScore" />
+          </lay-form-item>
+          <lay-form-item label="总评分≥" prop="sumScore">
+            <lay-input v-model="pageParams.sumScore" />
+          </lay-form-item>
+          <div class="form-button-wp">
+            <lay-button @click="getPageInfo()">搜索</lay-button>
+          </div>
+        </lay-form>
+      </div>
+      <div>
+        <lay-table :page="tablePage" size="sm" :columns="columns" resize :data-source="dataShowSource" :loading="pageConfig.loading" @change="handleCurrentChange" @sortChange="handleSortChange">
+          <template v-slot:msvScore="{ row }">
+            <span class="primary-color">{{ row.msv_score }}</span>
+          </template>
+          <template v-slot:liquidityScore="{ row }">
+            <span class="normal-color">{{ row.liquidity_score }}</span>
+          </template>
+          <template v-slot:frequencyScore="{ row }">
+            <span class="warm-color">{{ row.frequency_score }}</span>
+          </template>
+          <template v-slot:score="{ row }">
+            <span class="danger-color">{{ row.score }}</span>
+          </template>
+          <template v-slot:operator="{ row }">
+            <div>
+              <TableButton text="查看MSV" @click="toJump(row)" />
+            </div>
+          </template>
+        </lay-table>
+      </div>
+    </template>
+  </lay-card>
+</template>
+
+<script lang="ts" setup name="IndicatorSymbolRankReal">
+import { ref, reactive } from "vue";
+import { useRouter } from "vue-router";
+import TableButton from "@/components/TableButton.vue";
+import { get_exchange_rank, get_symbols_rank_real } from "@/api";
+
+const router = useRouter();
+
+interface PageConfig {
+  loading: boolean;
+}
+
+let pageConfig: PageConfig = reactive({
+  loading: false,
+});
+
+interface FormItem {
+  exchange?: string;
+  msvScore?: string;
+  liquidityScore?: string;
+  frequencyScore?: string;
+  sumScore?: string;
+}
+const pageParams: FormItem = reactive({ exchange: "gate_usdt_swap" });
+
+interface TablePage {
+  current: number;
+  limit: number;
+  total: number;
+}
+const tablePage: TablePage = reactive({ current: 1, limit: 20, total: 0, limits: [20, 50, 100, 200, 500] });
+const columns = ref([
+  { title: "币对", width: "110", key: "symbol", ellipsisTooltip: true },
+  { title: "最大Abs(波动率)", key: "msv_abs_max" },
+  { title: "平均Abs(波动率)", key: "msv_abs_avg" },
+  { title: "有效波动次数", key: "effective_count" },
+  { title: "平均行情推动量", key: "liquidity_avg" },
+  { title: "Sum(Abs(波动率))", key: "msv_abs_total" },
+  { title: "Sum(预期利润)", key: "epr_total" },
+  { title: "波动分数", width: "80", key: "msv_score", customSlot: "msvScore", ellipsisTooltip: true },
+  { title: "交易量分数", width: "100", key: "liquidity_score", customSlot: "liquidityScore" },
+  { title: "交易频次分数", width: "100", key: "frequency_score", customSlot: "frequencyScore" },
+  { title: "总评分", width: "80", key: "score", customSlot: "score", sort: "desc" },
+  { title: "开仓值", width: "90", key: "coverted_open_base", ellipsisTooltip: true },
+  { title: "操作", width: "90", key: "operator", customSlot: "operator" },
+]);
+let dataSource = ref<any>([]);
+let dataShowSource = ref<any>([]);
+let dataSortSource = ref<any>([]);
+let sortInfo = ref<any>({});
+
+let rkExchanges = ref<any>([]);
+
+const getRkExchanges = () => {
+  const params = {};
+  pageConfig.loading = true;
+  get_exchange_rank(params, (data: any) => {
+    pageConfig.loading = false;
+    if (data.code == 200) {
+      rkExchanges.value = data.data;
+    }
+  });
+};
+getRkExchanges();
+
+// 请求交易所列表
+const getPageInfo = () => {
+  tablePage.current = 1;
+  tablePage.total = 0;
+  dataSource.value = [];
+  const params = {
+    exchange: pageParams.exchange,
+  };
+  pageConfig.loading = true;
+  get_symbols_rank_real(params, (data: any) => {
+    pageConfig.loading = false;
+    if (data.code == 200) {
+      let source = data.data.filter((item: any) => {
+        let msvScore = parseFloat(pageParams.msvScore || "0");
+        let liquidityScore = parseFloat(pageParams.liquidityScore || "0");
+        let frequencyScore = parseFloat(pageParams.frequencyScore || "0");
+        let sumScore = parseFloat(pageParams.sumScore || "0");
+        return item.msv_score >= msvScore && item.liquidity_score >= liquidityScore && item.frequency_score >= frequencyScore && item.score >= sumScore;
+      });
+      tablePage.total = source.length;
+      dataSource.value = source;
+      handleSortChange(sortInfo.value.key, sortInfo.value.sort);
+    }
+  });
+};
+getPageInfo();
+
+const toJump = (value: any) => {
+  router.push({ path: "/indicator/msv", query: { symbol: value.symbol, exchange: pageParams.exchange, minute_time_range: 70 } });
+};
+
+// 分页设置
+const handleCurrentChange = (val: any) => {
+  dataShowSource.value = [...dataSortSource.value.slice((val.current - 1) * val.limit, val.current * val.limit)];
+};
+
+// 排序
+const handleSortChange = (key: any, sort: any) => {
+  sortInfo.value = { key, sort };
+  if (!sort) {
+    dataSortSource.value = [...dataSource.value];
+    dataShowSource.value = [...dataSource.value.slice((tablePage.current - 1) * tablePage.limit, tablePage.current * tablePage.limit)];
+    return;
+  }
+  dataSortSource.value = [...dataSource.value].sort((a: any, b: any) => {
+    let maxA = -9999999;
+    let maxB = -9999999;
+    Object.values(a[key]).map((item) => (maxA = Number(item) > maxA ? Number(item) : maxA));
+    Object.values(b[key]).map((item) => (maxB = Number(item) > maxB ? Number(item) : maxB));
+    return maxA - maxB;
+  });
+  if (sort == "desc") dataSortSource.value.reverse();
+  dataShowSource.value = [...dataSortSource.value.slice((tablePage.current - 1) * tablePage.limit, tablePage.current * tablePage.limit)];
+};
+</script>
+
+<style lang="scss" scoped>
+.custom-form-layout {
+  .custom-card-checkbox,
+  .custom-checkbox {
+    display: inline-flex;
+    align-items: center;
+    margin-bottom: 16px;
+    padding-right: 20px;
+    .label {
+      display: flex;
+      padding-right: 15px;
+    }
+    .checkbox-group {
+      display: flex;
+      .checkbox-wp {
+        display: flex;
+        align-items: center;
+        margin-right: 10px;
+        line-height: 38px;
+        :deep(.layui-checkbox-label) {
+          padding: 0;
+        }
+        :deep(.layui-form-radio) {
+          margin-top: 0;
+        }
+        :deep(.layui-checkcard) {
+          padding: 0 10px;
+          width: auto;
+          margin: 0 10px 0 0;
+          .layui-checkcard-content {
+            padding: 0;
+          }
+        }
+        .checkbox-input {
+          width: 40px;
+        }
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+  }
+  .custom-card-checkbox {
+    .checkbox-wp {
+      padding: 0 20px;
+      border: 1px solid #d9d9d9;
+    }
+  }
+}
+</style>