|
|
@@ -0,0 +1,243 @@
|
|
|
+<template>
|
|
|
+ <lay-card class="custom-card">
|
|
|
+ <template v-slot:title>
|
|
|
+ <span class="card-title">爆仓指标</span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-slot:body>
|
|
|
+ <lay-loading class="custom-loading" :loading="pageConfig.loading">
|
|
|
+ <div class="custom-form-layout">
|
|
|
+ <lay-form class="form-wp" :model="pageParams" mode="inline" size="sm">
|
|
|
+ <lay-form-item label="币对" prop="symbol">
|
|
|
+ <lay-input v-model="pageParams.symbol" />
|
|
|
+ </lay-form-item>
|
|
|
+ <lay-form-item label="盘口" prop="exchange">
|
|
|
+ <lay-select v-model="pageParams.exchange" :show-search="true" allowClear>
|
|
|
+ <lay-select-option v-for="item of iaExchanges" :value="item" :label="item" />
|
|
|
+ </lay-select>
|
|
|
+ </lay-form-item>
|
|
|
+ <lay-form-item label="查询时间" prop="minute_time_range">
|
|
|
+ <lay-input v-model="pageParams.minute_time_range" placeholder="查询时间(分钟)" />
|
|
|
+ </lay-form-item>
|
|
|
+ <lay-form-item label="阈值" prop="value_ln">
|
|
|
+ <lay-input v-model="pageParams.value_ln" placeholder="阈值" />
|
|
|
+ </lay-form-item>
|
|
|
+ <lay-form-item>
|
|
|
+ <lay-button @click="getMsvData()">查询</lay-button>
|
|
|
+ </lay-form-item>
|
|
|
+ </lay-form>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div class="chart" ref="chartRef"></div>
|
|
|
+ </div>
|
|
|
+ </lay-loading>
|
|
|
+ </template>
|
|
|
+ </lay-card>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup name="IndicatorMsv">
|
|
|
+import { ref, reactive, onUnmounted, shallowRef } from "vue";
|
|
|
+import * as echarts from "echarts";
|
|
|
+import { get_indicator, get_ia_exchanges } from "@/api";
|
|
|
+
|
|
|
+const chartRef = ref();
|
|
|
+const tradesRef = ref();
|
|
|
+
|
|
|
+interface PageConfig {
|
|
|
+ loading: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+interface FormItem {
|
|
|
+ symbol?: String;
|
|
|
+ exchange?: String;
|
|
|
+ minute_time_range?: String;
|
|
|
+ value_ln?: String;
|
|
|
+}
|
|
|
+
|
|
|
+let pageConfig: PageConfig = reactive({
|
|
|
+ loading: false,
|
|
|
+});
|
|
|
+
|
|
|
+let symbol: any = "btc_usdt";
|
|
|
+let exchange: any = "binance_usdt_swap";
|
|
|
+let minute_time_range: any = "30";
|
|
|
+let value_ln: any = "10";
|
|
|
+
|
|
|
+const pageParams: FormItem = reactive({ symbol, exchange, minute_time_range, value_ln });
|
|
|
+
|
|
|
+let iaExchanges = ref<any>([]);
|
|
|
+const getIaExchanges = () => {
|
|
|
+ const params = {};
|
|
|
+ pageConfig.loading = true;
|
|
|
+ get_ia_exchanges(params, (data: any) => {
|
|
|
+ pageConfig.loading = false;
|
|
|
+ if (data.code == 200) {
|
|
|
+ iaExchanges.value = data.data;
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+getIaExchanges();
|
|
|
+
|
|
|
+let msvChart = shallowRef();
|
|
|
+
|
|
|
+const getMsvData = () => {
|
|
|
+ const params = { indicator: "fot", query: pageParams };
|
|
|
+ pageConfig.loading = true;
|
|
|
+ get_indicator(params, (data: any) => {
|
|
|
+ pageConfig.loading = false;
|
|
|
+ if (data.code == 200) {
|
|
|
+ initChart(data.data);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+getMsvData();
|
|
|
+
|
|
|
+const initChart = (data: any) => {
|
|
|
+ if (msvChart.value != null && !msvChart.value.isDisposed()) echarts.dispose(msvChart.value);
|
|
|
+
|
|
|
+ msvChart.value = echarts.init(chartRef.value);
|
|
|
+
|
|
|
+ window.removeEventListener("resize", () => msvChart.value.resize());
|
|
|
+ window.addEventListener("resize", () => msvChart.value.resize());
|
|
|
+
|
|
|
+ let lineSeriesData = data.profits.map((item: any, index: number) => ({ name: `折线${index + 1}`, type: "line", symbol: "none", data: item }));
|
|
|
+ let barSeriesData = [
|
|
|
+ {
|
|
|
+ name: "柱状图",
|
|
|
+ type: "line",
|
|
|
+ color: "rgb(55, 162, 255)",
|
|
|
+ data: data.profit_total_list,
|
|
|
+ xAxisIndex: 1,
|
|
|
+ yAxisIndex: 1,
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ {
|
|
|
+ offset: 0,
|
|
|
+ color: "rgb(55, 162, 255)",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ offset: 1,
|
|
|
+ color: "rgb(55, 162, 255)",
|
|
|
+ },
|
|
|
+ ]),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ const chartOption = {
|
|
|
+ title: [
|
|
|
+ {
|
|
|
+ text: `${pageParams.exchange} ${pageParams.symbol?.toUpperCase()} ${parseFloat(((Number(pageParams.minute_time_range) || 240) / 60).toFixed(2)).toString()}小时波动幅度指标`,
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 20,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ grid: [
|
|
|
+ {
|
|
|
+ top: "100px",
|
|
|
+ left: "60px",
|
|
|
+ right: "60px",
|
|
|
+ height: "40%", // 主图高度
|
|
|
+ backgroundColor: "red",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ left: "60px",
|
|
|
+ right: "60px",
|
|
|
+ bottom: "100px",
|
|
|
+ height: "25%", // 副图高度
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ dataZoom: [
|
|
|
+ {
|
|
|
+ type: "inside",
|
|
|
+ xAxisIndex: [0, 1],
|
|
|
+ start: 0,
|
|
|
+ end: 100,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ xAxisIndex: [0, 1],
|
|
|
+ start: 0,
|
|
|
+ end: 100,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ axisPointer: {
|
|
|
+ type: "cross",
|
|
|
+ },
|
|
|
+ formatter: (params: any) => {
|
|
|
+ if (params.length === 0) return "";
|
|
|
+ },
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: "category",
|
|
|
+ boundaryGap: false,
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "category",
|
|
|
+ gridIndex: 1, // 垂直副图的网格索引
|
|
|
+ show: true, // 不显示副图的 x 轴
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: "value",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ gridIndex: 1, // 垂直副图的网格索引
|
|
|
+ type: "value", // 副图的 y 轴
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ series: [...lineSeriesData, ...barSeriesData],
|
|
|
+ };
|
|
|
+ msvChart.value.setOption(chartOption);
|
|
|
+ // 绑定鼠标点击事件
|
|
|
+ msvChart.value.getZr().on("click", (event: any) => {
|
|
|
+ const pointInPixel = [event.offsetX, event.offsetY];
|
|
|
+ const pointInGrid = msvChart.value?.convertFromPixel({ seriesIndex: 0 }, pointInPixel);
|
|
|
+
|
|
|
+ if (pointInGrid) {
|
|
|
+ const xIndex = Math.round(pointInGrid[0]);
|
|
|
+ const xAxisData = msvChart.value?.getOption().series[0].data[xIndex];
|
|
|
+ const time = xAxisData[0];
|
|
|
+ const query = {
|
|
|
+ exchange: pageParams.exchange,
|
|
|
+ symbol: pageParams.symbol,
|
|
|
+ start_time_mills: (time * 1 - 60000).toString(),
|
|
|
+ end_time_mills: (time * 1 + 60000).toString(),
|
|
|
+ };
|
|
|
+ tradesRef.value.show(query);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+onUnmounted(() => {
|
|
|
+ window.removeEventListener("resize", () => msvChart.value.resize());
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.custom-card {
|
|
|
+ min-width: 256px;
|
|
|
+}
|
|
|
+.chart {
|
|
|
+ padding-top: 20px;
|
|
|
+ min-height: 800px;
|
|
|
+}
|
|
|
+.robot-info-header {
|
|
|
+ background-color: white;
|
|
|
+ padding: 16px 24px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ .robot-name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ .robot-status {
|
|
|
+ span {
|
|
|
+ font-size: 12px;
|
|
|
+ padding-left: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|