运用nodejs的expres框架和vue+echarts实现股票价格数据可视化

优化之前需要操作服务器文件或者数据库的操作,这次直接用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来实现。如有需要后面会跟大家分享。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值