|
|
@@ -0,0 +1,311 @@
|
|
|
+<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="rangeTime">
|
|
|
+ <lay-date-picker v-model="pageParams.rangeTime" range type="datetime" :placeholder="['开始日期', '结束日期']" />
|
|
|
+ </lay-form-item>
|
|
|
+ <div class="form-button-wp">
|
|
|
+ <lay-button @click="getUserBalanceData()">查询</lay-button>
|
|
|
+ </div>
|
|
|
+ <lay-form-item>
|
|
|
+ <lay-button-group>
|
|
|
+ <lay-button type="default" @click="handleRangeTime(1)">本日</lay-button>
|
|
|
+ <lay-button type="default" @click="handleRangeTime(2)">本周</lay-button>
|
|
|
+ <lay-button type="default" @click="handleRangeTime(3)">本月</lay-button>
|
|
|
+ <lay-button type="default" @click="handleRangeTime(4)">上月</lay-button>
|
|
|
+ </lay-button-group>
|
|
|
+ </lay-form-item>
|
|
|
+ <lay-form-item>
|
|
|
+ <lay-button-group>
|
|
|
+ <lay-button type="default" @click="initChart(balanceData, 1)">按收益率</lay-button>
|
|
|
+ <lay-button type="default" @click="initChart(balanceData, 2)">按收益</lay-button>
|
|
|
+ <lay-button type="default" @click="initChart(balanceData, 3)">按资金</lay-button>
|
|
|
+ </lay-button-group>
|
|
|
+ </lay-form-item>
|
|
|
+ </lay-form>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div class="chart" ref="balanceChartRef"></div>
|
|
|
+ </div>
|
|
|
+ </lay-loading>
|
|
|
+ </template>
|
|
|
+ </lay-card>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup name="StatisticBalanceArbitrage">
|
|
|
+import { ref, reactive, onUnmounted, shallowRef } from "vue";
|
|
|
+import * as echarts from "echarts";
|
|
|
+import dayjs from "dayjs";
|
|
|
+import isoWeek from "dayjs/plugin/isoWeek";
|
|
|
+import Decimal from "decimal.js";
|
|
|
+import { get_arbitrage_balance } from "@/api";
|
|
|
+
|
|
|
+dayjs.extend(isoWeek);
|
|
|
+
|
|
|
+const balanceChartRef = ref();
|
|
|
+interface PageConfig {
|
|
|
+ loading: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+interface FormItem {
|
|
|
+ rangeTime: Array<string>;
|
|
|
+}
|
|
|
+
|
|
|
+let pageConfig: PageConfig = reactive({
|
|
|
+ loading: false,
|
|
|
+});
|
|
|
+
|
|
|
+const pageParams: FormItem = reactive({ rangeTime: [dayjs().subtract(24, "hour").format("YYYY-MM-DD HH:mm:ss"), dayjs().format("YYYY-MM-DD HH:mm:ss")] });
|
|
|
+
|
|
|
+let balanceChart = shallowRef();
|
|
|
+let balanceData = ref();
|
|
|
+let earningsRateData = ref<Array<any>>([]);
|
|
|
+let earningsData = ref<Array<any>>([]);
|
|
|
+let totalBalanceData = ref<Array<any>>([]);
|
|
|
+
|
|
|
+const getUserBalanceData = () => {
|
|
|
+ balanceData.value = [];
|
|
|
+ earningsRateData.value = [];
|
|
|
+ earningsData.value = [];
|
|
|
+ totalBalanceData.value = [];
|
|
|
+ const params = {
|
|
|
+ startTime: +dayjs(pageParams.rangeTime[0]),
|
|
|
+ endTime: +dayjs(pageParams.rangeTime[1]),
|
|
|
+ };
|
|
|
+ pageConfig.loading = true;
|
|
|
+ get_arbitrage_balance(params, async (data: any) => {
|
|
|
+ pageConfig.loading = false;
|
|
|
+ if (data.code == 200) {
|
|
|
+ balanceData.value = data.data;
|
|
|
+ await handleBalanceData(data.data);
|
|
|
+ initChart(balanceData.value, 1);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+getUserBalanceData();
|
|
|
+
|
|
|
+const handleEarningsData = (data: any) => {
|
|
|
+ let transferBalance = new Decimal(0);
|
|
|
+ let earningsList = data.map((item: any, index: any) => {
|
|
|
+ if (index == 0) return 0;
|
|
|
+ if (item.label !== 0) transferBalance = transferBalance.add(item.changeU);
|
|
|
+ return new Decimal(item.afterU || 0).minus(data[0].afterU).minus(transferBalance).toNumber();
|
|
|
+ });
|
|
|
+ return earningsList;
|
|
|
+};
|
|
|
+const handleEarningsRateData = (data: any) => {
|
|
|
+ let transferBalance = data[0].afterU;
|
|
|
+ let transferEarningsRate: any = 0;
|
|
|
+ let lastEarningsRate: any = 0;
|
|
|
+ let earningsList = data.map((item: any, index: any) => {
|
|
|
+ if (index == 0) return 0;
|
|
|
+ if (item.label !== 0) {
|
|
|
+ transferBalance = item.afterU;
|
|
|
+ transferEarningsRate = lastEarningsRate;
|
|
|
+ return transferEarningsRate
|
|
|
+ };
|
|
|
+ let earningsRate = new Decimal(item.afterU || 0).minus(transferBalance).div(transferBalance).mul(100).add(transferEarningsRate).toNumber();
|
|
|
+ lastEarningsRate = earningsRate
|
|
|
+ return earningsRate;
|
|
|
+ });
|
|
|
+ return earningsList;
|
|
|
+};
|
|
|
+const handleTotalBalanceData = (data: any) => {
|
|
|
+ return data.map((item: any) => item.afterU);
|
|
|
+};
|
|
|
+
|
|
|
+const handleBalanceData = (data: any) => {
|
|
|
+ return new Promise((resolve: any) => {
|
|
|
+ // 过滤无效数据
|
|
|
+ balanceData.value = { ...data, userList: data.botList.filter((item: any) => item.balance.length > 0) };
|
|
|
+ balanceData.value.botList.map((item: any) => {
|
|
|
+ let earningList: any = handleEarningsData(item.balance);
|
|
|
+ let earningRateList: any = handleEarningsRateData(item.balance);
|
|
|
+ let totalList: any = handleTotalBalanceData(item.balance);
|
|
|
+ earningsData.value.push({ name: item.name, type: "line", stack: item.name, data: earningList });
|
|
|
+ earningsRateData.value.push({ name: item.name, type: "line", stack: item.name, data: earningRateList });
|
|
|
+ totalBalanceData.value.push({ name: item.name, type: "line", stack: item.name, data: totalList });
|
|
|
+ });
|
|
|
+ resolve(true);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const handleRangeTime = (type: number) => {
|
|
|
+ const currentTime = +dayjs();
|
|
|
+ const diffTimestamp = +dayjs(currentTime).hour(10).minute(0).second(0).millisecond(0) - currentTime;
|
|
|
+ let timestamp = diffTimestamp <= 0 ? currentTime : currentTime - 1000 * 60 * 60 * 10;
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case 1:
|
|
|
+ pageParams.rangeTime = [`${dayjs(timestamp).hour(10).minute(0).second(0).millisecond(0)}`, `${dayjs()}`];
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ pageParams.rangeTime = [`${dayjs(timestamp).isoWeekday(1).hour(10).minute(0).second(0).millisecond(0)}`, `${dayjs()}`];
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ pageParams.rangeTime = [`${dayjs(timestamp).startOf("month").hour(10).minute(0).second(0).millisecond(0)}`, `${dayjs()}`];
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ let upMonthTime = +dayjs(timestamp).startOf("month") - 1;
|
|
|
+ pageParams.rangeTime = [`${dayjs(upMonthTime).startOf("month").hour(10).minute(0).second(0).millisecond(0)}`, `${dayjs().startOf("month").hour(10).minute(0).second(0).millisecond(0)}`];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ getUserBalanceData();
|
|
|
+};
|
|
|
+
|
|
|
+const initChart = (data: any, type: number) => {
|
|
|
+ if (balanceChart.value != null && !balanceChart.value.isDisposed()) echarts.dispose(balanceChart.value);
|
|
|
+ balanceChart.value = echarts.init(balanceChartRef.value);
|
|
|
+
|
|
|
+ window.removeEventListener("resize", () => balanceChart.value.resize());
|
|
|
+ window.addEventListener("resize", () => balanceChart.value.resize());
|
|
|
+
|
|
|
+ const xData = data.timeList.map((item: any) => dayjs(item * 1).format("MM-DD HH:mm"));
|
|
|
+ const legendData = data.userList.map((item: any) => item.name);
|
|
|
+ if (type == 1) {
|
|
|
+ const earningsRateOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ formatter: (value: any) => {
|
|
|
+ const axisLabel = `${value[0].axisValueLabel}`;
|
|
|
+ const info = value.map((item: any) => `${item.marker}${item.seriesName}: ${item.data.toFixed(2)}%`);
|
|
|
+ return `${axisLabel}<br/>${info.join("<br/>")}`;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ toolbox: {
|
|
|
+ feature: {
|
|
|
+ dataZoom: {},
|
|
|
+ brush: {
|
|
|
+ type: ["rect", "clear"],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: legendData,
|
|
|
+ top: "bottom",
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ data: xData,
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ min: "dataMin",
|
|
|
+ axisLabel: {
|
|
|
+ formatter: (value: any) => {
|
|
|
+ return `${value.toFixed(2)}%`;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: earningsRateData.value,
|
|
|
+ };
|
|
|
+ balanceChart.value.setOption(earningsRateOption);
|
|
|
+ }
|
|
|
+ if (type == 2) {
|
|
|
+ const earningsOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ formatter: (value: any) => {
|
|
|
+ const axisLabel = `${value[0].axisValueLabel}`;
|
|
|
+ const info = value.map((item: any) => `${item.marker}${item.seriesName}: ${item.data}`);
|
|
|
+ return `${axisLabel}<br/>${info.join("<br/>")}`;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ toolbox: {
|
|
|
+ feature: {
|
|
|
+ dataZoom: {},
|
|
|
+ brush: {
|
|
|
+ type: ["rect", "clear"],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: legendData,
|
|
|
+ top: "bottom",
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ data: xData,
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ min: "dataMin",
|
|
|
+ },
|
|
|
+ series: earningsData.value,
|
|
|
+ };
|
|
|
+ balanceChart.value.setOption(earningsOption);
|
|
|
+ }
|
|
|
+ if (type == 3) {
|
|
|
+ const earningsOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ formatter: (value: any) => {
|
|
|
+ const axisLabel = `${value[0].axisValueLabel}`;
|
|
|
+ const info = value.map((item: any) => `${item.marker}${item.seriesName}: ${item.data}`);
|
|
|
+ return `${axisLabel}<br/>${info.join("<br/>")}`;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ toolbox: {
|
|
|
+ feature: {
|
|
|
+ dataZoom: {},
|
|
|
+ brush: {
|
|
|
+ type: ["rect", "clear"],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: legendData,
|
|
|
+ top: "bottom",
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ data: xData,
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ min: "dataMin",
|
|
|
+ },
|
|
|
+ series: totalBalanceData.value,
|
|
|
+ };
|
|
|
+ balanceChart.value.setOption(earningsOption);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ window.removeEventListener("resize", () => balanceChart.value.resize());
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.custom-card {
|
|
|
+ min-width: 256px;
|
|
|
+ .form-button-wp {
|
|
|
+ margin-right: 60px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.chart {
|
|
|
+ padding-top: 20px;
|
|
|
+ min-height: 500px;
|
|
|
+}
|
|
|
+.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>
|