交易系统前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

719 lines
23 KiB

<template>
<card>
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-3">
<base-button @click="excelImport">导入数据源</base-button>
</div>
<div class="col-md-4">
<base-input>
<el-date-picker
class="select-danger"
v-model="startTime"
type="datetime"
placeholder="选择开始日期与时间"
>
</el-date-picker>
</base-input>
</div>
<div class="col-md-4">
<base-input>
<el-date-picker
v-model="endTime"
type="datetime"
placeholder="选择结束日期与时间"
@change="timepIkcer"
>
</el-date-picker>
</base-input>
</div>
</div>
</div>
<div class="col-md-6">
<el-select
class="select-danger"
style="width:200px"
placeholder="策略选择"
v-model="policyMode"
>
<el-option
v-for="item in RobotPolicyType"
class="select-danger"
:label="item.title"
:key="item.value"
:value="item.value"
>
</el-option>
</el-select>
<base-button @click="executeTest" class="ml-2">执行回测</base-button>
<base-button class="ml-2">重置回测</base-button>
</div>
</div>
<!-- <a-row type="flex">
<a-col :span="16">
<a-row type="flex"
><a-col><a-button @click="excelImport">导入数据源</a-button></a-col
><a-col :offset="1"
><a-date-picker v-model="startTime"></a-date-picker></a-col
><a-col :offset="1"
><a-date-picker v-model="endTime"></a-date-picker></a-col
><a-col :offset="1"
><a-select
v-model="policyMode"
placeholder="选择策略"
style="width:160px"
>
<a-select-option
v-for="i in RobotPolicyType"
:key="i.value"
:value="i.value"
>
{{ i.title }}</a-select-option
>
</a-select></a-col
>
</a-row></a-col
>
<a-col :span="4">
<a-row type="flex">
<a-col><a-button @click="executeTest">执行回测</a-button></a-col>
<a-col :offset="1"> <a-button>重置回测</a-button></a-col>
</a-row>
</a-col>
</a-row> -->
<div>
<el-tabs value="1">
<el-tab-pane name="1" label="K线图表">
<div class="row">
<div class="col-md-3">
<p class="form-label">文件列表</p>
<el-select
class="select-danger"
style="width:200px"
placeholder="不限"
v-model="lineSelect"
@change="onKlineExcelChange"
>
<el-option
v-for="item in kLinesFromExcels"
class="select-danger"
:label="item.name"
:key="item.name"
:value="item.name"
>
</el-option>
</el-select>
</div>
<div class="col-md-2">
<p class="form-label">购买信号数显示过滤:</p>
<el-select
class="select-danger"
style="width:100px"
placeholder="不限"
v-model="keyFilter.buy"
@change="changeFilter($event, 1)"
>
<el-option
v-for="item in buyPointCountList"
class="select-danger"
:value="item.value"
:label="item.title"
:key="item.value"
>
</el-option>
</el-select>
</div>
<div class="col-md-2">
<p class="form-label">卖出信号数显示过滤:</p>
<el-select
class="select-danger"
style="width:100px"
placeholder="不限"
v-model="keyFilter.sale"
@change="changeFilter($event, 0)"
>
<el-option
v-for="item in salePointCountList"
class="select-danger"
:value="item.value"
:label="item.title"
:key="item.value"
>
</el-option>
</el-select>
</div>
</div>
<!-- k线Excel选择 -->
<KLine :data="kLineData" :keyFilter="keyFilter" />
<!-- <HQKline /> -->
</el-tab-pane>
<el-tab-pane name="2" label="编译策略">
<movement-policy-form
v-if="policyMode == 0"
v-model="policyFormData"
/>
<!-- <top-end-policy v-else v-model="policyFormData" /> -->
</el-tab-pane>
</el-tabs>
</div>
<div>
<el-tabs value="1">
<!-- 交易清单 -->
<el-tab-pane name="1" label="交易清单">
<order-table v-model="tradeData"></order-table>
</el-tab-pane>
<!-- 信号记录 -->
<el-tab-pane name="2" label="信号记录">
根据时间范围筛选信号记录:
<!-- <a-range-picker
style="margin: 10px 0"
v-model="dateRange"
:show-time="{ format: 'HH:mm' }"
format="YYYY-MM-DD HH:mm"
:placeholder="['开始时间', '结束时间']"
@change="onOk"
/> -->
<el-date-picker
v-model="dateRange"
type="datetimerange"
range-separator="~"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="onOk"
>
</el-date-picker>
<custom-base-table
size="small"
:columns="logColumns"
:dataSource="logData.filter(logShowCondition)"
>
<template slot="LogType" slot-scope="text">
{{ LogTypes.filter(item => item.value == text)[0].title }}
</template>
</custom-base-table>
</el-tab-pane>
<!-- 绩效总结 -->
<el-tab-pane name="3" label="绩效总结">
<account-info v-model="accountInfo" />
</el-tab-pane>
</el-tabs>
</div>
</card>
</template>
<script>
/* eslint-disable */
import { tradeColumns, logColumns } from "../prePages/js/columns";
import Excel from "../../utils/ExcelUtils";
import KLine from "../../oldComponents/EchartTable/kLine";
// import HQKline from "../../oldComponents/EchartTable/hqKline";
import { secToMin } from "../../utils/TimeUtils";
import AccountInfo from "./AccountInfo";
import {
RobotPolicyType,
ResetMomentumWaveTest,
LogTypes
} from "../prePages/js/selectoptions";
import MovementPolicyForm from "../../components/MovementPolicyForm";
import moment from "moment";
import OrderTable from "./OrderTable";
import CustomBaseTable from "../../components/CustomBaseTable.vue";
// import TopEndPolicy from "../components/TopEndPolicy.vue";
export default {
components: {
KLine,
AccountInfo,
MovementPolicyForm,
OrderTable,
CustomBaseTable
// HQKline
// TopEndPolicy
},
data() {
return {
moment,
secToMin,
LogTypes,
spinning: false,
lineSelect: "",
kLinesFromExcels: [],
tradeColumns,
logColumns,
logData: [],
RobotPolicyType,
policyMode: 0,
tradeData: [],
startTime: null,
endTime: null,
kLineData: {},
keyFilter: {},
policyFormData: {},
kLineDataList: [],
dateRange: [],
accountInfo: {},
topTempDataList: [],
buyPointCountList: [],
salePointCountList: [],
logShowCondition: i => true
};
},
mounted() {
this.policyFormData = ResetMomentumWaveTest;
},
methods: {
onOk(value) {
this.logShowCondition = item =>
moment(item.CreateTime).isBetween(moment(value[0]), moment(value[1]));
},
changeFilter(value, type) {
console.log(value, type);
this.keyFilter = Object.assign({}, this.keyFilter);
},
splitData(rawData) {
var categoryData = [];
var keyPoint = [];
var values = [];
this.buyPointCountList = [];
this.salePointCountList = [];
for (var i = 0; i < rawData.length; i++) {
categoryData.push(rawData[i].splice(0, 1)[0]);
let buyKey = rawData[i].splice(4, 1)[0];
if (buyKey && JSON.stringify(buyKey) != "{}") {
keyPoint.push(buyKey);
if (!this.buyPointCountList.some(item => item.value == buyKey.value))
this.buyPointCountList.push(buyKey);
}
let saleKey = rawData[i].splice(4, 1)[0];
if (saleKey && JSON.stringify(saleKey) != "{}") {
keyPoint.push(saleKey);
if (
!this.salePointCountList.some(item => item.value == saleKey.value)
)
this.salePointCountList.push(saleKey);
}
values.push(rawData[i]);
}
console.log({
categoryData,
values,
keyPoint
});
return {
categoryData,
values,
keyPoint
};
},
//选择不同的excel表展示
onKlineExcelChange(model) {
console.log(model);
if (
this.kLinesFromExcels &&
this.kLinesFromExcels.length > 0 &&
this.kLinesFromExcels.some(item => item.name == model)
) {
let result = [];
if (this.policyMode == 0) {
let data = this.kLinesFromExcels.filter(item => item.name == model)[0]
.data;
let temp = this.mutateData(data); //分别操作出接口需要的data和图标data
result = this.splitData(temp.kLineData); //图表data处理
this.kLineDataList = temp.postData;
this.kLineData = result;
} else {
let index = 0;
for (let i = 0; i < this.kLinesFromExcels.length; i++) {
if (this.kLinesFromExcels[i].name == model) {
index = i;
break;
}
}
let temp = this.topMutateData(this.topTempDataList[index]);
result = this.splitData(temp.kLineData); //图表data处理
this.kLineData = result;
}
}
},
excelImport() {
let that = this;
let result = [];
this.spinning = true;
Excel.importExcel(data => {
that.spinning = false;
if (data && data[0]) {
if (this.policyMode == 1) {
this.kLinesFromExcels = data;
this.lineSelect = data[0].name;
let _data = [];
data.forEach(element => {
_data.push(element.data);
});
this.topEndMutateData(_data);
console.log("final", _data);
this.topTempDataList = _data;
let temp = that.topMutateData(_data[0]);
result = this.splitData(temp.kLineData); //图表data处理
this.kLineData = result;
} else {
this.kLinesFromExcels = data;
this.lineSelect = data[0].name;
let temp = that.mutateData(data[0].data); //分别操作出接口需要的data和图标data
result = this.splitData(temp.kLineData); //图表data处理
console.log("what i got", result);
this.kLineDataList = temp.postData;
this.kLineData = result;
}
}
});
},
//顶底策略数据处理
topEndMutateData(data) {
if (data && data[0]) {
let originalArray = data[0];
var offset = [];
for (let oi = 1; oi < originalArray.length; oi++) {
let target = originalArray[oi];
var redTotal = target[5] == 1 ? 1 : 0; //以首表Red[5]为初始值
var greenTotal = target[6] == 1 ? 1 : 0;
// console.log(
// "为时间戳:" + target[0] + "找匹配",
// " redTotal:" + redTotal
// );
for (let di = 1; di < data.length; di++) {
let r = this.getOffset(
offset,
target,
data,
oi,
di,
redTotal,
greenTotal
);
if (r && r.redTotal) redTotal = r.redTotal;
if (r && r.greenTotal) greenTotal = r.greenTotal;
}
target[7] = target[5] == 1 ? redTotal : 0;
target[8] = target[6] == 1 ? greenTotal : 0;
for (let tempIndex = 1; tempIndex < data.length; tempIndex++) {
if (data[tempIndex][oi + offset[tempIndex]]) {
let condition = data[tempIndex][oi + offset[tempIndex]][5] == 1;
data[tempIndex][oi + offset[tempIndex]][7] = condition
? redTotal
: 0;
let condition2 = data[tempIndex][oi + offset[tempIndex]][6] == 1;
data[tempIndex][oi + offset[tempIndex]][8] = condition2
? greenTotal
: 0;
}
}
}
}
},
//得出offset计算
getOffset(offset, target, source, tIndex, sIndex, redTotal, greenTotal) {
// console.log(offset);
if (offset[sIndex] == null || offset[sIndex] == undefined)
offset[sIndex] = 0;
// console.log(target[0], source[sIndex][tIndex + offset[sIndex]][0]);
//先判读同个索引是否对其有对应时间戳
if (!source[sIndex][tIndex + offset[sIndex]]) return;
if (target[0] == source[sIndex][tIndex + offset[sIndex]][0]) {
//相同索引有时间戳的
redTotal =
redTotal + (source[sIndex][tIndex + offset[sIndex]][5] == 1 ? 1 : 0);
greenTotal =
greenTotal +
(source[sIndex][tIndex + offset[sIndex]][6] == 1 ? 1 : 0);
// console.log(
// target[0],
// redTotal,
// source[sIndex][tIndex + offset[sIndex]][0],
// "找到相等的"
// );
return { redTotal, greenTotal };
} else if (target[0] > source[sIndex][tIndex + offset[sIndex]][0]) {
// console.log("目标大", offset[sIndex]);
//目标时间戳大于比对的时间戳
offset[sIndex] = offset[sIndex] + 1;
this.getOffset(
offset,
target,
source,
tIndex,
sIndex,
redTotal,
greenTotal
);
} else if (target[0] < source[sIndex][tIndex + offset[sIndex]][0]) {
offset[sIndex] = offset[sIndex] - 1;
// console.log("目标小", offset);
//目标时间戳小于比对的时间戳
return;
}
},
//动量回测数据处理
mutateData(data) {
let result = [];
let postResult = [];
if (data.length > 1) {
for (let i = 0; i < data.length; i++) {
if (i == 0) continue;
let subArray = [];
let postObj = { N: null };
for (let j = 0; j < data[i].length; j++) {
if (j == 0) {
if (
this.startTime &&
this.endTime &&
!moment(data[i][j] * 1000).isBetween(
moment(this.startTime),
moment(this.endTime)
)
) {
break;
} else if (
this.startTime &&
!moment(data[i][j] * 1000).isAfter(moment(this.startTime))
) {
break;
}
subArray[0] = moment(data[i][j] * 1000).format(
"YYYY-MM-DD HH:mm:ss"
);
postObj.T = data[i][j];
} else if (j == 1) {
//open
subArray[1] = data[i][j];
postObj.O = data[i][j];
} else if (j == 2) {
//highest
subArray[4] = data[i][j];
postObj.H = data[i][j];
} else if (j == 3) {
//lowest
subArray[3] = data[i][j];
postObj.L = data[i][j];
} else if (j == 4) {
//close
subArray[2] = data[i][j];
postObj.C = data[i][j];
} else if (j == 7) {
postObj.TP = data[i][j];
} else if (j == 13) {
if (data[i][j] != 0) postObj.N = 3;
} else if (j == 11) {
if (data[i][j] != 0) postObj.N = 2;
} else if (j == 15) {
subArray[5] = {};
if (data[i][j] != 0) {
postObj.N = 1;
subArray[5] = {
name: "卖出Point",
coord: [
`${moment(data[i][0] * 1000).format(
"YYYY-MM-DD HH:mm:ss"
)}`,
data[i][2]
],
value: "1",
itemStyle: {
color: "rgb(23,160,93)"
}
};
}
} else if (j == 14) {
subArray[6] = {};
if (data[i][j] != 0) {
postObj.N = 0;
subArray[6] = {
name: "买入Point",
symbol: "diamond",
symbolSize: [15, 25],
symbolOffset: [0, 25],
coord: [
`${moment(data[i][0] * 1000).format(
"YYYY-MM-DD HH:mm:ss"
)}`,
data[i][3]
],
value: "0",
itemStyle: {
color: "rgb(221,82,70)"
}
};
}
}
}
if (subArray.length > 0) result.push(subArray);
if (postObj.T != null) postResult.push(postObj);
}
}
return { kLineData: result, postData: postResult };
},
//订底回测数据处理
topMutateData(data) {
let result = [];
let postResult = [];
if (data.length > 1) {
for (let i = 0; i < data.length; i++) {
if (i == 0) continue;
let subArray = [];
let postObj = { N: null };
for (let j = 0; j < data[i].length; j++) {
if (j == 0) {
if (
this.startTime &&
this.endTime &&
!moment(data[i][j] * 1000).isBetween(
this.startTime.startOf("day"),
this.endTime.endOf("day")
)
) {
break;
} else if (
this.startTime &&
!moment(data[i][j] * 1000).isAfter(
this.startTime.startOf("day")
)
) {
break;
}
subArray[0] = moment(data[i][j] * 1000).format(
"YYYY-MM-DD HH:mm:ss"
);
postObj.T = data[i][j];
} else if (j == 1) {
//open
subArray[1] = data[i][j];
postObj.OpenPrice = data[i][j];
} else if (j == 2) {
//highest
subArray[4] = data[i][j];
postObj.H = data[i][j];
} else if (j == 3) {
//lowest
subArray[3] = data[i][j];
postObj.L = data[i][j];
} else if (j == 4) {
//close
subArray[2] = data[i][j];
postObj.C = data[i][j];
} else if (j == 5) {
postObj.TP = data[i][j];
} else if (j == 6) {
if (data[i][j] != 0) postObj.N = 3;
} else if (j == 7) {
subArray[5] = {};
if (data[i][j] && data[i][j] != 0) {
postObj.N = 1;
subArray[5] = {
name: "买入Point",
coord: [
`${moment(data[i][0] * 1000).format(
"YYYY-MM-DD HH:mm:ss"
)}`,
data[i][3]
],
value: data[i][j],
itemStyle: {
color: "rgb(23,160,93)"
}
};
}
} else if (j == 8) {
subArray[6] = {};
if (data[i][j] && data[i][j] != 0) {
postObj.N = 0;
subArray[6] = {
name: "卖出Point",
symbol: "diamond",
symbolSize: [15, 25],
symbolOffset: [0, 25],
coord: [
`${moment(data[i][0] * 1000).format(
"YYYY-MM-DD HH:mm:ss"
)}`,
data[i][2]
],
value: data[i][j],
itemStyle: {
color: "rgb(221,82,70)"
}
};
}
}
}
if (subArray.length > 0) result.push(subArray);
if (postObj.T != null) postResult.push(postObj);
}
}
return { kLineData: result, postData: postResult };
},
executeTest() {
if (this.policyFormData && this.kLineDataList.length > 0) {
this.spinning = true;
this.$http
.post("/Api/Simulation/MomentumWaveTest", {
MomentumWavePolicy: this.policyFormData,
kLineDataList: this.kLineDataList
})
.then(res => {
this.logShowCondition = i => true;
this.spinning = false;
if (res.Code == 200) {
console.log("回测成功");
this.logData = res.Data.ExecutionLogList;
if (this.logData.length > 0) {
this.dateRange[0] = this.logData[
this.logData.length - 1
].CreateTime;
this.dateRange[1] = this.logData[0].CreateTime;
}
res.Data.OrderList.sort(
(a, b) => b.PurchaseKLineId - a.PurchaseKLineId
);
this.tradeData = res.Data.OrderList;
//accountInfo
this.accountInfo.MaxOrderTotalPurchasePricePerRound =
res.Data.MaxOrderTotalPurchasePricePerRound;
this.accountInfo.MaxTotalPurchasePricePerDay =
res.Data.MaxTotalPurchasePricePerDay;
this.accountInfo.MaxOrderHoldTime = res.Data.MaxOrderHoldTime;
this.accountInfo.MinOrderHoldTime = res.Data.MinOrderHoldTime;
this.accountInfo.MaxOrderLossRatio = res.Data.MaxOrderLossRatio;
this.accountInfo.MaxAccountLossRatio =
res.Data.MaxAccountLossRatio;
this.accountInfo.MaxOrderProfit = res.Data.MaxOrderProfit;
this.accountInfo.MaxOrderProfitRatio =
res.Data.MaxOrderProfitRatio;
this.accountInfo.MinOrderProfit = res.Data.MinOrderProfit;
this.accountInfo.MinOrderProfitRatio =
res.Data.MinOrderProfitRatio;
}
});
}
},
timepIkcer() {
console.log(this.startTime, this.endTime);
}
}
};
</script>
<style></style>