优化之前需要操作服务器文件或者数据库的操作,这次直接用nodejs写get端口响应从聚宽平台获取到的实时交易数据。
以下是服务器端完整代码,已经将数据处理成需要的json文件,通过get请求直接响应到前端。前端是利用vue来开发,实现数据的实时刷新。
const request = require('request');
const express = require('express')
const app=express()
app.use(express.static(__dirname+'/static-1'))
app.get('/test',(req,res)=>{
res.json({name:'jack',age:18,sex:'男'});
})
app.listen(5000,(err)=>{
if(!err) console.log('服务器启动成功')
})
/
var url1 = "https://dataapi.joinquant.com/apis";
var arrPrice = []//价格数据
var requestData = {
"method": "get_token",
"mob": "*****",
"pwd": "******" //密码和账号
};
function updata() {
request({
url: url1,
method: "POST",
body: JSON.stringify(requestData)
}, function (error, response, token) {
var requestData = {
"method": "get_price_period",
"token": token,
"code": "IF9999.CCFX",
"unit": "1m",
"date": "2021-11-11 09:30:00",
"end_date": "2021-11-11 15:30:00",
"fq_ref_date": "2018-12-18"
};
request({
url: url1,
method: "POST",
body: JSON.stringify(requestData)
}, function (error, response, body) {
var arr = body.split('\n')//把字符串转化成数组
//先分离标题
var arrNtitle = arr.splice(1)//只有时间和价格的数组
var arrUsed = []
for (let i = 0, length = arrNtitle.length; i < length; i++) {
arrUsed.push(arrNtitle[i].split(','))
}
// 把时间提取出来变成一个单独数组 并和价格数据一一对应
var arrData = [] //纯时间坐标
arrUsed.forEach(function (el) {//截取时间
arrData.push(el.shift())
})
arrUsed.forEach(function (el) {
let a = []
el.map(function (el) {
a.push(parseInt(el * 10) / 10)
})
let [op, cp, hp, lp] = a
arrPrice.push([op, cp, hp, lp])
})
for (let i = 0; i < arrData.length; i++) {
arrPrice[i].unshift(arrData[i])//把data加入元素
}
//发送get
app.get('/list',(req,res)=>{
res.json({list:arrPrice});
})
});
});
}
updata()
接下来就是vue的部分,全部代码都在这里,当然main.js的入口文件设置这里就不多赘述了,要引入axios组件,并在全局挂载。同时安装echarts的组件库,如何使用echarts请参考官方文档。
具体的请求和渲染方法可以看下面的代码,这里要提醒大家的一点是:即便是让获取数据的函数在更早一些的created钩子函数里,渲染函数在counted里,也不见得渲染函数能获取到数据。所以最保险的也是最简单的办法,就是像我那样,直接在获取数据后,确认data里有值了再调用echarts。
<template>
<div id="main"></div>
</template>
<script>
import echarts from "echarts";
export default {
data() {
return {
dlist: null,
charting: null,
};
},
methods: {
//定时更新
// updataTimer() {
// setInterval(() => {
// this.getInfo();
// }, 5000);
// },
//请求数据
async getInfo() {
const { data: list } = await this.$http.get("/list");
this.dlist = list.list;
if (this.dlist) {
//请求到数据后直接调用函数 不然画图函数获取到的数据为空
this.bing();
}
},
watch() {
dlist: "bing";
},
//画图
bing() {
this.charting = echarts.init(document.getElementById("main"));
var option;
var upColor = "#ec0000";
var upBorderColor = "#8A0000";
var downColor = "#00da3c";
var downBorderColor = "#008F28";
// 数据意义:开盘(open),收盘(close),最低(lowest),最高(highest)
var data0 = splitData(
this.dlist //数据
);
function splitData(rawData) {
var categoryData = [];
var values = [];
for (var i = 0; i < rawData.length; i++) {
categoryData.push(rawData[i].splice(0, 1)[0]);
values.push(rawData[i]);
}
return {
categoryData: categoryData,
values: values,
};
}
function calculateMA(dayCount) {
var result = [];
for (var i = 0, len = data0.values.length; i < len; i++) {
if (i < dayCount) {
result.push("-");
continue;
}
var sum = 0;
for (var j = 0; j < dayCount; j++) {
sum += data0.values[i - j][1];
}
result.push(sum / dayCount);
}
return result;
}
option = {
title: {
text: "IF",
left: 0,
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
},
},
legend: {
data: ["日K", "MA5", "MA10", "MA20", "MA30"],
},
grid: {
left: "10%",
right: "10%",
bottom: "15%",
},
xAxis: {
type: "category",
data: data0.categoryData,
scale: true,
boundaryGap: false,
axisLine: { onZero: false },
splitLine: { show: false },
splitNumber: 20,
min: "dataMin",
max: "dataMax",
},
yAxis: {
scale: true,
splitArea: {
show: true,
},
},
dataZoom: [
{
type: "inside",
start: 50,
end: 100,
},
{
show: true,
type: "slider",
top: "90%",
start: 50,
end: 100,
},
],
series: [
{
name: "日K",
type: "candlestick",
data: data0.values,
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upBorderColor,
borderColor0: downBorderColor,
},
markPoint: {
label: {
normal: {
formatter: function (param) {
return param != null ? Math.round(param.value) : "";
},
},
},
data: [
{
name: "XX标点",
coord: ["2013/5/31", 2300],
value: 2300,
itemStyle: {
color: "rgb(41,60,85)",
},
},
{
name: "highest value",
type: "max",
valueDim: "highest",
},
{
name: "lowest value",
type: "min",
valueDim: "lowest",
},
{
name: "average value on close",
type: "average",
valueDim: "close",
},
],
tooltip: {
formatter: function (param) {
return param.name + "<br>" + (param.data.coord || "");
},
},
},
markLine: {
symbol: ["none", "none"],
data: [
[
{
name: "from lowest to highest",
type: "min",
valueDim: "lowest",
symbol: "circle",
symbolSize: 10,
label: {
show: false,
},
emphasis: {
label: {
show: false,
},
},
},
{
type: "max",
valueDim: "highest",
symbol: "circle",
symbolSize: 10,
label: {
show: false,
},
emphasis: {
label: {
show: false,
},
},
},
],
{
name: "min line on close",
type: "min",
valueDim: "close",
},
{
name: "max line on close",
type: "max",
valueDim: "close",
},
],
},
},
{
name: "MA5",
type: "line",
data: calculateMA(5),
smooth: true,
lineStyle: {
opacity: 0.5,
},
},
{
name: "MA10",
type: "line",
data: calculateMA(10),
smooth: true,
lineStyle: {
opacity: 0.5,
},
},
{
name: "MA20",
type: "line",
data: calculateMA(20),
smooth: true,
lineStyle: {
opacity: 0.5,
},
},
{
name: "MA30",
type: "line",
data: calculateMA(30),
smooth: true,
lineStyle: {
opacity: 0.5,
},
},
],
};
option && this.charting.setOption(option);
},
},
created() {
// this.updataTimer();
this.getInfo();
},
mounted() {
this.$nextTick(() => {
this.bing();
});
},
updated() {
this.bing();
},
};
</script>
<style scoped>
#main {
height: 700px;
width: 100%;
}
</style>
最后要提醒大家的是:这是部署到服务器上的,如果本地运行vue文件请求服务器是有跨域问题的。所以大家要把vue文件打包成静态资源部署到本地服务器再在浏览器打开就ok了。
如果有同学不熟悉怎样启动服务器和部署,请私信。
上边是启动服务器的命令 ,你要有node环境这个不必多说。
这是2021-11-11当天的真实交易数据,可以将日期设置成当天,这样就可以实时获取交易数据。搭配自己的交易算法和交易规则,实现简单的半自动化交易。
后面有时间还要完善前后的交互,比如查询时间,增加算法比如波浪理论,背离理论算法,均线交叉等这些胜率很高的自我实现类指标。结合直观的图形化界面,辅助做出交易判断。
需要补充的一点是,这种交易辅助对于波动较大,有杠杆的多空都可以的期货,期权交易比较适合,毕竟我一直都是研究期权交易的。
如果有其他前端的知识大家都可以交流,算法类最终还是需要强类型语言比如python来实现。如有需要后面会跟大家分享。