Browse Source

短期胜率补充

master
lizhixin 4 years ago
parent
commit
4548cb3486
  1. 118
      package-lock.json
  2. 1
      package.json
  3. 34
      src/App.vue
  4. 5
      src/layout/dashboard/DashboardLayout.vue
  5. 74
      src/pages/ReCheck/AccountInfo.vue
  6. 75
      src/pages/ReCheck/OrderTable.vue
  7. 651
      src/pages/ReCheck/index.vue
  8. 201
      src/pages/WinRate/index.vue
  9. 7
      src/router/routes.js
  10. 41
      vue.config.js

118
package-lock.json

@ -11375,6 +11375,124 @@
}
}
},
"uglifyjs-webpack-plugin": {
"version": "2.2.0",
"resolved": "https://registry.npm.taobao.org/uglifyjs-webpack-plugin/download/uglifyjs-webpack-plugin-2.2.0.tgz",
"integrity": "sha1-51vIDn8ZN/cllUybTFoeln6p0Nc=",
"dev": true,
"requires": {
"cacache": "^12.0.2",
"find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0",
"schema-utils": "^1.0.0",
"serialize-javascript": "^1.7.0",
"source-map": "^0.6.1",
"uglify-js": "^3.6.0",
"webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0"
},
"dependencies": {
"find-cache-dir": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-2.1.0.tgz",
"integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=",
"dev": true,
"requires": {
"commondir": "^1.0.1",
"make-dir": "^2.0.0",
"pkg-dir": "^3.0.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz?cache=0&sync_timestamp=1597169842138&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-3.0.0.tgz",
"integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz",
"integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-2.1.0.tgz?cache=0&sync_timestamp=1587567572251&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmake-dir%2Fdownload%2Fmake-dir-2.1.0.tgz",
"integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz",
"integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"pkg-dir": {
"version": "3.0.0",
"resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-3.0.0.tgz?cache=0&sync_timestamp=1602859045787&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpkg-dir%2Fdownload%2Fpkg-dir-3.0.0.tgz",
"integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=",
"dev": true,
"requires": {
"find-up": "^3.0.0"
}
},
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-1.0.0.tgz",
"integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=",
"dev": true,
"requires": {
"ajv": "^6.1.0",
"ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.nlark.com/semver/download/semver-5.7.1.tgz?cache=0&sync_timestamp=1618847119601&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-5.7.1.tgz",
"integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=",
"dev": true
},
"serialize-javascript": {
"version": "1.9.1",
"resolved": "https://registry.npm.taobao.org/serialize-javascript/download/serialize-javascript-1.9.1.tgz?cache=0&sync_timestamp=1599740650381&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fserialize-javascript%2Fdownload%2Fserialize-javascript-1.9.1.tgz",
"integrity": "sha1-z8IArvd7YAxH2pu4FJyUPnmML9s=",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz?cache=0&sync_timestamp=1571657176668&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsource-map%2Fdownload%2Fsource-map-0.6.1.tgz",
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
"dev": true
},
"uglify-js": {
"version": "3.13.7",
"resolved": "https://registry.nlark.com/uglify-js/download/uglify-js-3.13.7.tgz?cache=0&sync_timestamp=1621369323186&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fuglify-js%2Fdownload%2Fuglify-js-3.13.7.tgz",
"integrity": "sha1-JUaKOzmxyHXfA/CTeytwNqk/P+4=",
"dev": true
}
}
},
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.nlark.com/unbox-primitive/download/unbox-primitive-1.0.1.tgz",

1
package.json

@ -39,6 +39,7 @@
"@vue/cli-service": "^4.4.6",
"node-sass": "^4.14.1",
"sass-loader": "^8.0.2",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-template-compiler": "^2.6.11"
},

34
src/App.vue

@ -6,23 +6,27 @@
</template>
<script>
export default {
methods: {
disableRTL() {
if (!this.$rtl.isRTL) {
this.$rtl.disableRTL();
}
},
toggleNavOpen() {
let root = document.getElementsByTagName('html')[0];
root.classList.toggle('nav-open');
export default {
methods: {
disableRTL() {
if (!this.$rtl.isRTL) {
this.$rtl.disableRTL();
}
},
mounted() {
this.$watch('$route', this.disableRTL, { immediate: true });
this.$watch('$sidebar.showSidebar', this.toggleNavOpen)
toggleNavOpen() {
let root = document.getElementsByTagName("html")[0];
root.classList.toggle("nav-open");
}
};
},
mounted() {
this.$watch("$route", this.disableRTL, { immediate: true });
this.$watch("$sidebar.showSidebar", this.toggleNavOpen);
}
};
</script>
<style lang="scss"></style>
<style lang="scss">
.el-tabs__item {
color: #C9CCD2;
}
</style>

5
src/layout/dashboard/DashboardLayout.vue

@ -12,6 +12,11 @@
:name="'账号管理'"
icon="tim-icons icon-single-02"
/>
<sidebar-link
to="/reCheck"
:name="'回测'"
icon="tim-icons icon-single-02"
/>
<sidebar-link
to="/winRate"
:name="'短线胜率测试'"

74
src/pages/ReCheck/AccountInfo.vue

@ -0,0 +1,74 @@
<template>
<div>
<base-input
label=" 单轮趋势最大持仓金额"
v-model="accountInfo.MaxOrderTotalPurchasePricePerRound"
disabled
></base-input>
<base-input
label=" 单日持仓最大金额(当天内购买未在当天内出售)"
v-model="accountInfo.MaxTotalPurchasePricePerDay"
disabled
></base-input>
<base-input
label=" 订单持仓最长时间"
v-model="accountInfo.MaxTotalPurchasePricePerDay"
disabled
></base-input>
<p>
订单持仓最长时间{{
accountInfo.MaxOrderHoldTime && secToMin(accountInfo.MaxOrderHoldTime)
}}
</p>
<p>
订单持仓最短时间{{
accountInfo.MinOrderHoldTime && secToMin(accountInfo.MinOrderHoldTime)
}}
</p>
<p>
订单最大浮动亏损比{{
accountInfo.MaxOrderLossRatio &&
accountInfo.MaxOrderLossRatio.toFixed(2)
}}%
</p>
<p>
持仓最大浮动亏损比{{
accountInfo.MaxAccountLossRatio &&
accountInfo.MaxAccountLossRatio.toFixed(2)
}}%
</p>
<p>
最大盈利金额订单盈利占比{{
accountInfo.MaxOrderProfit && accountInfo.MaxOrderProfit.toFixed(4)
}}({{
accountInfo.MaxOrderProfitRatio &&
accountInfo.MaxOrderProfitRatio.toFixed(2)
}}%)
</p>
<p>
最小盈利金额订单盈利占比{{
accountInfo.MinOrderProfit && accountInfo.MinOrderProfit.toFixed(4)
}}({{
accountInfo.MinOrderProfitRatio &&
accountInfo.MinOrderProfitRatio.toFixed(2)
}}%)
</p>
</div>
</template>
<script>
import { secToMin } from "../../utils/TimeUtils";
export default {
model: { prop: "accountInfo", event: "accountChange" },
props: { accountInfo: { type: Object, default: () => {} } },
data() {
return { secToMin };
}
};
</script>
<style></style>

75
src/pages/ReCheck/OrderTable.vue

@ -0,0 +1,75 @@
<template>
<custom-base-table
:data="data"
:columns="columns"
thead-classes="text-primary"
>
<!-- 卖卖类型 -->
<template slot="OrderType">
<p>买入</p>
<p>卖出</p>
</template>
<!-- 买卖时间 -->
<template slot="KLineId" slot-scope="item">
<p>
{{
moment(item.row.item.PurchaseKLineId * 1000).format("YYYY-MM-DD HH:mm:ss")
}}
</p>
<p>
{{
item.row.item.SaleKLineId
? moment(item.row.item.SaleKLineId * 1000).format("YYYY-MM-DD HH:mm:ss")
: "-"
}}
</p>
</template>
<!-- 买卖价格 -->
<template slot="Price" slot-scope="item">
<p>{{ item.row.item.PurchasePrice || "-" }}</p>
<p>{{ item.row.item.SalePrice || "-" }}</p>
</template>
<!-- 买卖数量 -->
<template slot="Count" slot-scope="item">
<p>{{ item.row.item.PurchaseCoinCount || "-" }}</p>
<p>{{ item.row.item.SaleCoinCount || "-" }}</p>
</template>
<!-- 最大浮亏 -->
<template slot="MaxLossRatio" slot-scope="item">
<p>{{ item.row.item.MaxLossPrice || "-" }}</p>
<p>{{ item.row.item.MaxLossRatio || "-" }}%</p>
</template>
<!-- 盈亏 -->
<template slot="Profit" slot-scope="item">
<p>{{ item.row.item.Profit }}</p>
<p v-if="item.row.item.Profit">
{{ ((item.row.item.Profit / item.row.item.TotalPurchasePrice) * 100).toFixed(2) }}%
</p>
<p v-else>-</p>
</template>
<!-- 累计盈亏 -->
<template slot="TotalProfit" slot-scope="item">
<p>{{ item.row.item.TotalProfit }}</p>
<p v-if="item.row.item.TotalProfit">
{{
((item.row.item.TotalProfit / policyFormData.PositionFund) * 100).toFixed(2)
}}%
</p>
<p v-else>-</p>
</template>
</custom-base-table>
</template>
<script>
import CustomBaseTable from "../../components/CustomBaseTable";
import { tradeColumns } from "../../pages/prePages/js/columns";
import moment from "moment";
export default {
model: { prop: "data", event: "tableDataChange" },
props: { data: { type: Array, default: () => [] } },
components: CustomBaseTable,
data() {
return { moment, columns: tradeColumns };
}
};
</script>

651
src/pages/ReCheck/index.vue

@ -0,0 +1,651 @@
<template>
<card>
<!-- <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 default-active-key="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="编译策略">
<MomentumWavePolicy v-if="policyMode == 0" v-model="policyFormData" />
<!-- <top-end-policy v-else v-model="policyFormData" /> -->
</el-tab-pane>
</el-tabs>
</div>
<div>
<el-tabs default-active-key="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="['开始时间', '结束时间']"
@ok="onOk"
/>
<a-table
size="small"
:columns="logColumns"
:dataSource="logData.filter(logShowCondition)"
>
<template slot="LogType" slot-scope="text">
{{ LogTypes.filter(item => item.value == text)[0].title }}
</template>
</a-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 MomentumWavePolicy from "../../components/MovementPolicyForm";
import moment from "moment";
import OrderTable from "./OrderTable";
// import TopEndPolicy from "../components/TopEndPolicy.vue";
export default {
components: {
KLine,
AccountInfo,
MomentumWavePolicy,
OrderTable
// 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: ResetMomentumWaveTest,
kLineDataList: [],
dateRange: [],
accountInfo: {},
topTempDataList: [],
buyPointCountList: [],
salePointCountList: [],
logShowCondition: i => true
};
},
methods: {
onOk(value) {
this.logShowCondition = item =>
moment(item.CreateTime).isBetween(value[0], 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); //datadata
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); //datadata
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(
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.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] = moment(
this.logData[this.logData.length - 1].CreateTime
);
this.dateRange[1] = moment(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;
}
});
}
}
}
};
</script>
<style></style>

201
src/pages/WinRate/index.vue

@ -3,45 +3,103 @@
<base-button @click="excelImport">导入Excel</base-button>
<span v-if="name" class="ml-4">导入成功{{ name }}</span>
<div class="row">
<div class="col-md-1">
<div class="col-md-2">
<base-radio name="radio0" class="mb-3" key="radio0" v-model="radio">
固定止盈占比
固定止盈占比%
</base-radio>
</div>
<div class="col-md-3">
<base-input
placeholder="固定止盈占比"
placeholder="固定止盈占比(%)"
v-model="form.deadProfitRatio"
></base-input>
</div>
</div>
<div class="row">
<div class="col-md-1">
<div class="col-md-2">
<base-radio name="radio1" class="mb-3" key="radio1" v-model="radio">
盈亏占比
盈亏占比%
</base-radio>
</div>
<div class="col-md-3">
<base-input
placeholder="盈亏占比"
placeholder="盈亏占比(%)"
v-model="form.profitRatio"
></base-input>
</div>
</div>
<base-input
placeholder="滑点比例设置"
label="滑点设置(影响买入价格) 单位:%"
v-model="form.sliderRatio"
></base-input>
<div class="row">
<div class="col-md-2">
<base-input
placeholder="滑点比例设置"
label="滑点设置(影响买入价格) 单位:%"
v-model="form.sliderRatio"
></base-input>
</div>
<div class="col-md-3">
<base-input
placeholder="过滤信号比(%)"
label="过滤信号比(%)"
v-model="form.filterRatio"
></base-input>
</div>
<div class="col-md-3">
<base-input
placeholder="止损放大比(%)"
label="止损放大比(%)"
v-model="form.lostFloatRatio"
></base-input>
</div>
</div>
<base-button @click="winRateTest">胜率测试</base-button>
<base-input
label="交易次数"
v-model="result.tradeCount"
disabled
></base-input>
<base-input label="交易胜率" v-model="result.winRate" disabled></base-input>
<base-input
label="交易败率"
label="交易胜率(%)"
v-model="result.winRate"
disabled
></base-input>
<base-input
label="最高连续止盈次数"
v-model="result.maxWin"
disabled
></base-input>
<div class="row">
<div class="col-md-3">
<base-input
label="最高连续止损次数"
v-model="result.maxLose"
disabled
></base-input>
</div>
<div class="col-md-3">
<base-input
label="平均亏损百分比(%)"
v-model="result.avgLost"
disabled
></base-input>
</div>
<div class="col-md-3">
<base-input
label="最大亏损百分比(%)"
v-model="result.maxLostRate"
disabled
></base-input>
</div>
<div class="col-md-3">
<base-input
label="累计亏损百分比(%)"
v-model="result.totalLostRate"
disabled
></base-input>
</div>
</div>
<base-input
label="交易败率(%)"
v-model="result.loseRate"
disabled
></base-input>
@ -58,9 +116,9 @@ export default {
return {
radio: "radio0",
name: "",
form: { deadProfitRatio: 0.3, sliderRatio: 0 },
form: { deadProfitRatio: 0.3, sliderRatio: 0, lostFloatRatio: 0 },
data: [],
result: {}
result: { maxWin: 0, maxLose: 0 }
};
},
methods: {
@ -92,24 +150,44 @@ export default {
rs();
});
},
//time open high low close
//time[0] open[1] high[2] low[3] close[4]
winRateTest() {
this.validateForm().then(rs => {
let tradeCount = 0;
let win = 0;
let lose = 0;
let tempWin = 0;
let tempLose = 0;
let maxLose = 0;
let maxWin = 0;
let maxLostRate = 0;
let totalLostRate = 0;
for (let i = 0; i < this.data.length; i++) {
if (this.isSignal(this.data[i])) {
let { up, low } = this.isSignal(this.data[i]);
if (!up && !low) {
//doing nothing
} else {
if (this.form.filterRatio) {
let condition = up
? (this.data[i][4] - this.data[i][1]) / this.data[i][1]
: (this.data[i][1] - this.data[i][4]) / this.data[i][1];
// console.log("", condition);
if (condition > this.form.filterRatio / 100) continue;
}
tradeCount++;
let result;
if (up) {
let buyPrice =
this.data[i][4] * (1 + this.form.sliderRatio / 100); // *
let stopLost = this.data[i][3]; //
let stopLost =
this.data[i][3] -
Math.abs(
((this.data[i][4] - this.data[i][1]) *
this.form.lostFloatRatio) /
100
); //
let stopWin;
//
if (this.radio == "radio0")
@ -120,21 +198,26 @@ export default {
(buyPrice - stopLost) * (this.form.profitRatio / 100) +
buyPrice;
console.log(buyPrice, stopWin, stopLost);
let result = this.reachEnd(
// console.log(buyPrice, stopWin, stopLost);
result = this.reachEnd(
0,
i + 1,
stopWin,
stopLost,
this.data[i][0]
this.data[i][0],
buyPrice
);
if (result == 1) win++;
if (result == -1) lose++;
}
if (low) {
let buyPrice =
this.data[i][4] * (1 - this.form.sliderRatio / 100); // *
let stopLost = this.data[i][2]; //
let stopLost =
this.data[i][2] +
Math.abs(
((this.data[i][1] - this.data[i][4]) *
this.form.lostFloatRatio) /
100
); //
let stopWin;
//
if (this.radio == "radio0")
@ -145,16 +228,29 @@ export default {
buyPrice -
(buyPrice - stopLost) * (this.form.profitRatio / 100);
console.log(buyPrice, stopWin, stopLost);
let result = this.reachEnd(
// console.log(buyPrice, stopWin, stopLost);
result = this.reachEnd(
1,
i + 1,
stopWin,
stopLost,
this.data[i][0]
this.data[i][0],
buyPrice
);
if (result == 1) win++;
if (result == -1) lose++;
}
if (result == 1) {
win++;
tempLose = 0;
tempWin++;
if (maxWin < tempWin) maxWin = tempWin;
}
if (result < 0) {
lose++;
tempWin = 0;
tempLose++;
if (maxLose < tempLose) maxLose = tempLose;
if (maxLostRate > result) maxLostRate = result;
totalLostRate += result;
}
}
}
@ -162,9 +258,14 @@ export default {
this.result = {
tradeCount,
winRate: ((win / tradeCount) * 100).toFixed(2),
loseRate: ((lose / tradeCount) * 100).toFixed(2)
loseRate: ((lose / tradeCount) * 100).toFixed(2),
maxWin,
maxLose,
totalLostRate: totalLostRate.toFixed(2),
maxLostRate: maxLostRate.toFixed(2),
avgLost: (totalLostRate / lose).toFixed(2)
};
console.log(tradeCount, win);
// console.log(tradeCount, win);
});
},
@ -178,48 +279,52 @@ export default {
},
// type: 0 1
reachEnd(type, index, stopWin, stopLost, timeStamp) {
reachEnd(type, index, stopWin, stopLost, timeStamp, buyPrice) {
let result = 0;
if (type == 0) {
console.log(
`=====================================================(${timeStamp})做多信号开始===止盈${stopWin}====止损${stopLost}========================================`
);
// console.log(
// `=====================================================${timeStamp}===${stopWin}====${stopLost}========================================`
// );
//
for (let i = index; i < this.data.length; i++) {
if (this.data[i][2] > stopWin) {
console.log(`---->触发止盈点`, this.data[i]);
// console.log(`---->`, this.data[i]);
result = 1;
break;
} else if (this.data[i][3] < stopLost) {
console.log(`---->触发止损点`, this.data[i]);
result = -1;
// console.log(`---->`, this.data[i]);
let lostPercent = ((stopLost - buyPrice) / buyPrice) * 100;
// console.log("", lostPercent);
result = lostPercent;
break;
} else {
console.log(`->未触发止盈止损点`, this.data[i]);
// console.log(`->`, this.data[i]);
}
}
} else {
console.log(
`=====================================================(${timeStamp})做空信号开始===止盈${stopWin}====止损${stopLost}========================================`
);
// console.log(
// `=====================================================${timeStamp}===${stopWin}====${stopLost}========================================`
// );
//
for (let i = index; i < this.data.length; i++) {
if (this.data[i][3] < stopWin) {
console.log(`---->触发止盈点`, this.data[i]);
// console.log(`---->`, this.data[i]);
result = 1;
break;
} else if (this.data[i][2] > stopLost) {
console.log(`---->触发止损点`, this.data[i]);
result = -1;
// console.log(`---->`, this.data[i]);
let lostPercent = ((buyPrice - stopLost) / buyPrice) * 100;
// console.log("", lostPercent, buyPrice, stopLost);
result = lostPercent;
break;
} else {
console.log(`->未触发止盈止损点`, this.data[i]);
// console.log(`->`, this.data[i]);
}
}
}
console.log(
`=====================================================信号交易结束===止盈${stopWin}====止损${stopLost}========================================`
);
// console.log(
// `========================================================${stopWin}====${stopLost}========================================`
// );
return result;
},
reachFail() {},

7
src/router/routes.js

@ -14,7 +14,7 @@ const Robot = () => import("@/pages/Robot");
const Account = () => import("@/pages/Account");
const Edit = () => import("@/pages/Robot/edit.vue");
const WinRate = () => import("@/pages/WinRate");
const ReCheck = () => import("@/pages/ReCheck");
const routes = [
{
path: "/",
@ -40,6 +40,11 @@ const routes = [
path: "account",
name: "account",
component: Account
},
{
path: "reCheck",
name: "reCheck",
component: ReCheck
}
// {
// path: "profile",

41
vue.config.js

@ -1,37 +1,52 @@
const webpack = require('webpack');
const webpack = require("webpack");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
lintOnSave: false,
configureWebpack: {
// Set up all the aliases we use in our app.
resolve: {
alias: {
'chart.js': 'chart.js/dist/Chart.js'
"chart.js": "chart.js/dist/Chart.js"
}
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 6
})
]
],
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false
},
compress: {
drop_debugger: true,
drop_console: true
}
}
})
]
}
},
pwa: {
name: 'Vue Black Dashboard',
themeColor: '#344675',
msTileColor: '#344675',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: '#344675'
name: "Vue Black Dashboard",
themeColor: "#344675",
msTileColor: "#344675",
appleMobileWebAppCapable: "yes",
appleMobileWebAppStatusBarStyle: "#344675"
},
pluginOptions: {
i18n: {
locale: 'en',
fallbackLocale: 'en',
localeDir: 'locales',
locale: "en",
fallbackLocale: "en",
localeDir: "locales",
enableInSFC: false
}
},
css: {
// Enable CSS source maps.
sourceMap: process.env.NODE_ENV !== 'production'
sourceMap: process.env.NODE_ENV !== "production"
}
};

Loading…
Cancel
Save