Merge branch 'dev' of https://github.com/vnpy/vnpy into dev

This commit is contained in:
vn.py 2018-02-13 08:04:10 +08:00
commit 925940996e
3 changed files with 212 additions and 112 deletions

View File

@ -57,7 +57,7 @@ def generateVtBar(row):
bar.close = row['close'] bar.close = row['close']
bar.volume = row['volume'] bar.volume = row['volume']
bar.date = str(row['trade_date']) bar.date = str(row['date'])
bar.time = str(row['time']).rjust(6, '0') bar.time = str(row['time']).rjust(6, '0')
#将bar的时间改成提前一分钟 #将bar的时间改成提前一分钟

View File

@ -45,7 +45,7 @@ with open("WEB_setting.json") as f:
# 创建Flask对象 # 创建Flask对象
from flask import Flask from flask import Flask, send_file
from flask.ext.restful import Api, Resource, reqparse from flask.ext.restful import Api, Resource, reqparse
from flask.ext.socketio import SocketIO from flask.ext.socketio import SocketIO
from flask_cors import * from flask_cors import *
@ -593,6 +593,12 @@ class CtaStrategyVar(Resource):
l = engine.getStrategyVar(name) l = engine.getStrategyVar(name)
return {'result_code':'success','data':l} return {'result_code':'success','data':l}
########################################################################
@app.route('/')
def index_html():
"""首页"""
return send_file( os.path.abspath('.') + '/web/index.html' )
# 注册资源 # 注册资源
api.add_resource(Token, '/token') api.add_resource(Token, '/token')

View File

@ -10,13 +10,26 @@
<body> <body>
<div id="app"> <div id="app">
<el-dialog title="请登录" :visible.sync="LoginFormVisible" :close-on-click-modal="false" :show-close ="false" :lock-scroll="true">
<el-form :model="loginForm">
<el-form-item label="用户名" :label-width="formLabelWidth">
<el-input v-model="loginForm.user" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="密码" :label-width="formLabelWidth">
<el-input v-model="loginForm.pwd" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="onLogin">确 定</el-button>
</div>
</el-dialog>
<template> <template>
<el-tabs v-model="activeTab" @tab-click="handleTab"> <el-tabs v-model="activeTab" @tab-click="handleTab">
<el-tab-pane label="交易" name="home"> <el-tab-pane label="交易" name="home">
<el-container style="margin-top: 12px;"> <el-container style="margin-top: 12px;">
<el-aside class="leftTrade" :style="{ height: leftTradeHeight, width:'320px'}"> <el-aside class="leftTrade" :style="{ height: leftTradeHeight, width:'320px'}">
<el-row> <el-row>
<el-form class="el-col el-col el-col-24" ref="form" :model="form" :rules="formRules" label-width="50px" show-message="true" inline-message="true" validate="formValidate"> <el-form class="el-col el-col el-col-24" ref="form" :model="form" label-width="50px" show-message="true" inline-message="true" validate="formValidate">
<div class="el-form-item"> <div class="el-form-item">
<label class="el-form-item__label" style="width: 50px;">代码</label> <label class="el-form-item__label" style="width: 50px;">代码</label>
<div class="el-form-item__content" style="margin-left: 50px;"> <div class="el-form-item__content" style="margin-left: 50px;">
@ -36,10 +49,10 @@
</el-input> </el-input>
<p>卖五</p> <p>卖五</p>
</el-form-item> --> </el-form-item> -->
<el-form-item label="名称"> <!--<el-form-item label="名称">
<el-input class="el-input--suffix" v-model="form.name"></el-input> <el-input class="el-input--suffix" v-model="form.name"></el-input>
<p>卖四</p> <p>卖四</p>
</el-form-item> </el-form-item> -->
<el-form-item label="方向类型"> <el-form-item label="方向类型">
<el-select v-model="form.direction" placeholder="请选择"> <el-select v-model="form.direction" placeholder="请选择">
<el-option label="多" value="DIRECTION_LONG"></el-option> <el-option label="多" value="DIRECTION_LONG"></el-option>
@ -73,19 +86,20 @@
</el-select> </el-select>
<p>买一 {{leftTrade.bidPrice1}} {{leftTrade.bidVolume1}}</p> <p>买一 {{leftTrade.bidPrice1}} {{leftTrade.bidVolume1}}</p>
</el-form-item> </el-form-item>
<el-form-item label="交易接口"> <!--<el-form-item label="交易接口">
<el-select v-model="form.region" placeholder="请选择"> <el-select v-model="form.region" placeholder="请选择">
<el-option label="区域一" value="shanghai"></el-option> <el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option> <el-option label="区域二" value="beijing"></el-option>
</el-select> </el-select>
<p>买二</p> <p>买二</p>
</el-form-item> </el-form-item>-->
</el-form> </el-form>
</el-row> </el-row>
<el-row class="body-left__btn"> <el-row class="body-left__btn">
<div class="el-col el-col-18"> <div class="el-col el-col-18">
<el-button class="el-col el-col-24" type="primary" @click="onSubmit" v-loading="loading.order">发单</el-button> <el-button class="el-col el-col-24" type="primary" @click="onSubmit" v-loading="loading.order">发单</el-button>
<el-button class="el-col el-col-24">全撤</el-button> <el-button class="el-col el-col-24">全撤</el-button>
<el-button class="el-col el-col-24" v-loading="loading.token" @click="gGateway">连接CTP</el-button>
</div> </div>
</el-row> </el-row>
<el-row class="body-left__connection"> <el-row class="body-left__connection">
@ -295,10 +309,10 @@
<el-row> <el-row>
<el-col :span='24'> <el-col :span='24'>
<el-button type="primary" plain @click="doOnLoad">加载策略</el-button> <el-button type="primary" plain @click="doOnLoad">加载策略</el-button>
<el-button type="success" plain>全部初始化</el-button> <el-button type="success" plain @click="doAction('','init')">全部初始化</el-button>
<el-button type="danger" plain>全部启动</el-button> <el-button type="danger" plain @click="doAction('','start')">全部启动</el-button>
<el-button type="warning" plain>全部停止</el-button> <el-button type="warning" plain @click="doAction('','stop')">全部停止</el-button>
<el-button type="info" plain>保持持仓</el-button> <!--<el-button type="info" plain>保持持仓</el-button>-->
</el-col> </el-col>
<el-col :span='24' class="cta-frame cta-frame__top" :style="{ height: topFrame}" v-loading="loading.strategy"> <el-col :span='24' class="cta-frame cta-frame__top" :style="{ height: topFrame}" v-loading="loading.strategy">
<div class="cta-frame__top_section" v-for="item,index in strategy"> <div class="cta-frame__top_section" v-for="item,index in strategy">
@ -317,7 +331,7 @@
</template> </template>
<template> <template>
<el-table :data="[item.var]" stripe style="width: 100%"> <el-table :data="[item.var]" stripe style="width: 100%">
<el-table-column :pro="subindex" :label="subindex" v-for="(subitem, subindex) in item.var" width="120"> <el-table-column :pro="subindex" :label="subindex" v-for="(subitem, subindex) in item.var" width="120">
<template scope="scope"> <template scope="scope">
{{[item.var][scope.$index][subindex]}} {{[item.var][scope.$index][subindex]}}
</template> </template>
@ -348,16 +362,17 @@
<!-- 引入组件库 --> <!-- 引入组件库 -->
<script src="https://cdn.bootcss.com/element-ui/2.1.0/index.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.1.0/index.js"></script>
<script> <script>
var socket = io.connect("http://127.0.0.1:5000", { transports: ['websocket'] }); var host = "http://127.0.0.1:5000"
var socket = io.connect(host + "", { transports: ['websocket'] });
var _strategy = new Object() var _strategy = new Object()
new Vue({ new Vue({
el: '#app', el: '#app',
data: function() { data: function() {
return { return {
table_height: (window.innerHeight - 70) / 3 - 15 || 250, table_height: (window.innerHeight - 70) / 3 - 15 || 250,
winHeight:window.innerHeight, winHeight: window.innerHeight,
topFrame:window.innerHeight*2/3 - 65 + "px", topFrame: window.innerHeight * 2 / 3 - 65 + "px",
bottomFrame:window.innerHeight/3 - 65 + "px", bottomFrame: window.innerHeight / 3 - 65 + "px",
leftTradeHeight: (window.innerHeight - 50) + 'px', leftTradeHeight: (window.innerHeight - 50) + 'px',
selected_data: [], selected_data: [],
form: { form: {
@ -368,23 +383,12 @@ new Vue({
offset: '', offset: '',
priceType: '', priceType: '',
}, },
formRules: { loginForm: {
vtSymbol: [ user: "",
{ required: true, message: '请输入代码', trigger: 'blur' } pwd: ""
],
lastPrice: [
{ required: true, message: '请输入代码', trigger: 'blur' }
],
direction: [
{ required: true, message: '请选择方向类型', trigger: 'change' }
],
offset: [
{ required: true, message: '请输入开平', trigger: 'change' }
],
priceType: [
{ required: true, message: '请输入价格类型', trigger: 'change' }
]
}, },
LoginFormVisible: true,
formLabelWidth: '120px',
eAccount: [], eAccount: [],
eTick: [], eTick: [],
tickObj: {}, tickObj: {},
@ -403,17 +407,17 @@ new Vue({
leftTrade: {}, //存放左侧交易区显示数据 leftTrade: {}, //存放左侧交易区显示数据
config: {}, //存放token等数据 config: {}, //存放token等数据
strategy: {}, //策略名 strategy: {}, //策略名
ctaLog:[], ctaLog: [],
loading: { loading: {
subScribe: false, subScribe: false,
order: false, order: false,
contract: false, contract: false,
strategy:false, strategy: false,
token: false,
}, },
} }
}, },
created: function() { created: function() {
this.gToken()
this.gAccount() this.gAccount()
this.gConnectionStatus() this.gConnectionStatus()
this.gTick() this.gTick()
@ -422,50 +426,98 @@ new Vue({
this.gError() this.gError()
this.gOrder() this.gOrder()
this.gTrade() this.gTrade()
const that = this;
that.onLoadInfo('account', 'account')
that.onLoadInfo('trades', 'trade')
that.onLoadInfo('position', 'position')
that.onLoadInfo('log', 'log')
that.onLoadInfo('error', 'error')
}, },
methods: { methods: {
onLogin() {
console.log(this.loginForm.user)
let userName = this.loginForm.user,
pwd = this.loginForm.pwd;
this.gToken(userName, pwd)
},
doAction(e,type){ gToken(username, password) {
let name = e, that = this, info=""; const that = this;
switch(type){ that.loading.token = true;
case "init": info = "初始化" axios.get(host + '/token?username=' + username + '&password=' + password)
break; .then(function(res) {
case "stop": info = "停止" that.loading.token = false;
break; if (res.status == 200 && res.data.result_code == "success") {
case "start": info = "启动" that.$message({ message: '已登录', type: 'success' });
break; that.config.token = res.data.data
that.LoginFormVisible = false
that.onLoadInfo('account', 'account')
that.onLoadInfo('trades', 'trade')
that.onLoadInfo('position', 'position')
that.onLoadInfo('log', 'log')
that.onLoadInfo('error', 'error')
} else {
that.$notify({ title: '警告', message: 'token获取失败用户名或密码错误', type: 'warning', duration: 0, });
}
})
.catch(function(err) {
that.loading.token = false;
that.$notify({ title: '警告', message: 'token获取失败连接错误', type: 'warning', duration: 0, });
});
},
doAction(e, type) {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
} }
axios.post("http://127.0.0.1:5000/ctastrategy/" + type,{ let name = e,
name:name, that = this,
token:this.config.token info = "";
}) switch (type) {
.then(res=>{ case "init":
if (res.data.result_code =="success") { info = "初始化"
that.$message({ message: name + info + '成功', type: 'success' }); break;
if (type == "start") { case "stop":
that.gCtaStrategy() info = "停止"
break;
case "start":
info = "启动"
break;
}
axios.post(host + "/ctastrategy/" + type, {
name: name,
token: this.config.token
})
.then(res => {
if (res.data.result_code == "success") {
that.$message({ message: name + info + '成功', type: 'success' });
if (type == "start") {
that.gCtaStrategy()
}
} else {
that.$message({ message: name + info + '成功', type: 'fail' });
} }
}else{ })
that.$message({ message: name + info +'成功', type: 'fail' }); .catch(res => {
} that.$message({ message: name + info + '成功', type: 'fail' });
}) })
.catch(res=>{
that.$message({ message: name + info +'成功', type: 'fail' });
})
}, },
doOnLoad() { doOnLoad() {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
const that = this; const that = this;
let strategy = {}; let strategy = {};
that.loading.strategy = true; that.loading.strategy = true;
axios.post('http://127.0.0.1:5000/ctastrategy/load', { axios.post(host + '/ctastrategy/load', {
token: this.config.token token: this.config.token
}) })
.then(res => { .then(res => {
@ -483,9 +535,17 @@ new Vue({
}) })
}, },
gVar(name) { gVar(name) {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
const that = this, const that = this,
_var = new Object(); _var = new Object();
axios.get("http://127.0.0.1:5000/ctastrategy/var?name=" + name + "&token=" + this.config.token) axios.get(host + "/ctastrategy/var?name=" + name + "&token=" + this.config.token)
.then(res => { .then(res => {
if (res.data.result_code !== "success") { if (res.data.result_code !== "success") {
that.$notify({ title: '警告', message: 'ctastrategy/var接口报错', type: 'warning', duration: 0, }); that.$notify({ title: '警告', message: 'ctastrategy/var接口报错', type: 'warning', duration: 0, });
@ -520,8 +580,16 @@ new Vue({
}, },
gParams(name) { gParams(name) {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
const that = this; const that = this;
axios.get("http://127.0.0.1:5000/ctastrategy/param?name=" + name + "&token=" + this.config.token) axios.get(host + "/ctastrategy/param?name=" + name + "&token=" + this.config.token)
.then(res => { .then(res => {
if (res.data.result_code !== "success") { if (res.data.result_code !== "success") {
that.$notify({ title: '警告', message: 'ctastrategy/param接口报错', type: 'warning', duration: 0, }); that.$notify({ title: '警告', message: 'ctastrategy/param接口报错', type: 'warning', duration: 0, });
@ -534,7 +602,6 @@ new Vue({
}) })
.catch(res => { .catch(res => {
console.log(res)
that.$notify({ title: '警告', message: 'ctastrategy/param接口报错', type: 'warning', duration: 0, }); that.$notify({ title: '警告', message: 'ctastrategy/param接口报错', type: 'warning', duration: 0, });
}) })
}, },
@ -545,7 +612,7 @@ new Vue({
if (this.config.token !== undefined) { if (this.config.token !== undefined) {
gInfo(apiName, this.config.token) gInfo(apiName, this.config.token)
} else { } else {
axios.get('http://127.0.0.1:5000/token?username=test&password=test', { axios.get(host + '/token?username=test&password=test', {
}) })
.then(function(res) { .then(function(res) {
@ -562,7 +629,7 @@ new Vue({
} }
function gInfo(apiName, token) { function gInfo(apiName, token) {
axios.get("http://127.0.0.1:5000/" + apiName + "?token=" + token) axios.get(host + "/" + apiName + "?token=" + token)
.then(res => { .then(res => {
if (res.data.result_code == "success") { if (res.data.result_code == "success") {
that[dataArr] = that[dataArr].concat(res.data.data).reverse() that[dataArr] = that[dataArr].concat(res.data.data).reverse()
@ -577,8 +644,16 @@ new Vue({
}, },
onSubmit(e) { onSubmit(e) {
this.loading.order = true; this.loading.order = true;
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
const that = this; const that = this;
axios.post('http://127.0.0.1:5000/order', { axios.post(host + '/order', {
vtSymbol: this.form.vtSymbol, vtSymbol: this.form.vtSymbol,
price: this.form.lastPrice, price: this.form.lastPrice,
volume: this.form.volume, volume: this.form.volume,
@ -601,6 +676,14 @@ new Vue({
}); });
}, },
onSubscribe(target) { onSubscribe(target) {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
const that = this; const that = this;
if (typeof(target) !== "object") { if (typeof(target) !== "object") {
var _vtSymbol = target var _vtSymbol = target
@ -616,7 +699,7 @@ new Vue({
}); });
} else { } else {
this.loading.subScribe = true; this.loading.subScribe = true;
axios.post("http://127.0.0.1:5000/tick", { axios.post(host + "/tick", {
vtSymbol: _vtSymbol, vtSymbol: _vtSymbol,
token: this.config.token token: this.config.token
}) })
@ -638,9 +721,17 @@ new Vue({
} }
}, },
dOrder(e) { dOrder(e) {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
let vtOrderID = e.vtOrderID, let vtOrderID = e.vtOrderID,
that = this; that = this;
axios.delete("http://127.0.0.1:5000/order?vtOrderID=" + vtOrderID + "&token=" + this.config.token) axios.delete(host + "/order?vtOrderID=" + vtOrderID + "&token=" + this.config.token)
.then(res => { .then(res => {
if (res.data.result_code == "success") { if (res.data.result_code == "success") {
that.$message({ message: '撤单已提交', type: 'success' }); that.$message({ message: '撤单已提交', type: 'success' });
@ -652,21 +743,6 @@ new Vue({
that.$notify({ title: '警告', message: '服务异常,撤单提交失败', type: 'warning', duration: 2000, }); that.$notify({ title: '警告', message: '服务异常,撤单提交失败', type: 'warning', duration: 2000, });
}) })
}, },
gToken() {
const that = this;
axios.get('http://127.0.0.1:5000/token?username=test&password=test')
.then(function(res) {
if (res.status == 200 && res.data.result_code == "success") {
that.config.token = res.data.data
that.gGateway(res.data.data)
} else {
that.$notify({ title: '警告', message: 'token获取失败', type: 'warning', duration: 0, });
}
})
.catch(function(err) {
that.$notify({ title: '警告', message: 'token获取失败', type: 'warning', duration: 0, });
});
},
handleClick(row) { handleClick(row) {
this.onSubscribe(row.vtSymbol) this.onSubscribe(row.vtSymbol)
}, },
@ -675,30 +751,47 @@ new Vue({
that.loading.contract = true; that.loading.contract = true;
if (tab.name == "search") { if (tab.name == "search") {
this.gContract() this.gContract()
}else if(tab.name == "cta"){ } else if (tab.name == "cta") {
this.judgeIfStrategy() this.judgeIfStrategy()
this.gCtaLog() this.gCtaLog()
} }
}, },
judgeIfStrategy(){ judgeIfStrategy() {
if (this.config.token == undefined || this.config.token == "") {
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
let that = this; let that = this;
axios.get("http://127.0.0.1:5000/ctastrategy/name?token="+this.config.token) axios.get(host + "/ctastrategy/name?token=" + this.config.token)
.then(res=>{ .then(res => {
let nameArray = res.data.data; let nameArray = res.data.data;
nameArray.forEach(function(item){ nameArray.forEach(function(item) {
_strategy[item] = { name: item }; _strategy[item] = { name: item };
that.gParams(item) that.gParams(item)
that.gVar(item) that.gVar(item)
}) })
}) })
.catch(res=>{ .catch(res => {
console.log(res) that.$notify({ title: '警告', message: '连接异常', type: 'warning', duration: 4500, });
}) })
}, },
gContract(){ gContract() {
if (this.config.token == undefined || this.config.token == "") {
this.loading.contract = false;
this.$alert('请先连接CTP', '警告', {
confirmButtonText: '确定',
});
return;
}
const that = this; const that = this;
if (that.contract.length == 0) { if (that.contract.length == 0) {
axios.get("http://127.0.0.1:5000/contract?token=" + this.config.token) axios.get(host + "/contract?token=" + this.config.token)
.then(res => { .then(res => {
that.loading.contract = false; that.loading.contract = false;
if (res.data.result_code == "success") { if (res.data.result_code == "success") {
@ -713,14 +806,17 @@ new Vue({
} }
}, },
gGateway(token) { gGateway() {
axios.post('http://127.0.0.1:5000/gateway', { const that = this;
token: token, axios.post(host + '/gateway', {
token: this.config.token,
gatewayName: 'CTP' gatewayName: 'CTP'
}) })
.then(function(res) { .then(function(res) {
that.$message({ message: '已连接CTP', type: 'success' });
}) })
.catch(function(err) { .catch(function(err) {
console.log("res",err)
}); });
}, },
gTick(target) { gTick(target) {
@ -748,7 +844,7 @@ new Vue({
socket.on("eTick.", function(data) { socket.on("eTick.", function(data) {
that.leftTrade = that.tickObj[target] that.leftTrade = that.tickObj[target]
that.leftTrade.priceRatio = ((that.tickObj[target].lastPrice / that.tickObj[target].preClosePrice) - 1).toFixed(2) + "%" that.leftTrade.priceRatio = (((that.tickObj[target].lastPrice / that.tickObj[target].preClosePrice) - 1)*100.0).toFixed(2) + "%"
// if (that.tickObj[target] !== undefined) { // if (that.tickObj[target] !== undefined) {
// if (that.tickObj[target].lastPrice == undefined) { // if (that.tickObj[target].lastPrice == undefined) {
// that.form.lastPrice = that.tickObj[target].lastPrice // that.form.lastPrice = that.tickObj[target].lastPrice
@ -770,15 +866,15 @@ new Vue({
accounts = new Object(); accounts = new Object();
socket.on("eAccount.", function(data) { socket.on("eAccount.", function(data) {
accounts[data.orderID] = data accounts[data.orderID] = data
that.account = that.account.concat(Object.values(accounts)).reverse() that.account = Object.values(accounts).reverse()
}); });
}, },
gPosition() { gPosition() {
let that = this, let that = this,
positions = new Object(); positions = new Object();
socket.on("ePosition.", function(data) { socket.on("ePosition.", function(data) {
positions[data.symbol] = data positions[data.vtSymbol] = data
that.position = that.position.concat(Object.values(positions)).reverse() that.position = Object.values(positions).reverse()
}); });
}, },
gTrade() { gTrade() {
@ -803,13 +899,11 @@ new Vue({
logObj = new Object(); logObj = new Object();
socket.on("eLog.", function(data) { socket.on("eLog.", function(data) {
logObj[data.logTime] = data logObj[data.logTime] = data
that.log = that.concat(Object.values(logObj)).reverse() that.log = that.log.concat(Object.values(logObj)).reverse()
}); });
}, },
handleTabLeft(tab, event) { handleTabLeft(tab, event) {},
}, handleTabRight(tab, event) {},
handleTabRight(tab, event) {
},
gConnectionStatus() { gConnectionStatus() {
const that = this; const that = this;
socket.on('disconnect', function() { socket.on('disconnect', function() {
@ -856,4 +950,4 @@ new Vue({
}) })
</script> </script>
</html> </html>