用python开发了一个绘制股票k线图的工具,还可以预测股票涨跌!【文末附源码和教学视频】

文章目录

    • 聊一聊这个工具
    • 效果展示
    • 股票数据
    • 运行项目
    • 前端界面
    • 后端接口
    • 源码地址

聊一聊这个工具

起初,我并不在意echarts,这不过是一个偶然、一次选择、一条简单的代码、一个图表的诞生,直到我完成了K线图的绘制。

股票,一个神奇的发明,他看不见摸不着,无法预测,却时刻让你的神经紧绷。他的出现,是对打工人的福音?还是一场灾难?是命运的转折点,还是一夜回到解放前。他都可以帮你实现。

K线图,他让你的投资有了参考,花哨的图表下面隐藏着财富的密码。接下来就让我们一起揭开这神秘的面纱。

效果展示


输入你喜欢的股票代码,就可以生成近一年的K线图。如此方便简洁,难免不让人心动。

更加令人兴奋的是,根据算法,可以预测出明天股票的涨跌情况。当然准不准我也不知道,仅供参考。

股票数据

该工具使用的股票数据来自于Tushare,只需要注册获取一个token就可以免费得到股票数据。

Tushare官网地址:https://tushare.pro/document/2?doc_id=14

运行项目

第一步: 从文末的源码地址,下载项目并解压到本地电脑

第二步: 配置运行环境,使用pycharm导入项目,在【terminal】下运行下面命令:

pip install -r requirements.txt

如果下载第三方库比较慢,可以考虑换一下pip的下载源:

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

阿里云 http://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) http://pypi.douban.com/simple/
清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/

第三步: 更改app.py中的ts_token,使用自己在tushare网站注册获取的token。右击运行app.py在浏览器中就可以看到图表。

前端界面

前端的技术主要用到echarts、element、vue等技术,官方文档上多有详细的案例,只需要参照官方文档案例代码,根据自己需求修改调试好代码即可。

绘制K线图

echarts图表案例:Examples - Apache ECharts

elements组件案例:组件 | Element

源码展示:

<html>
	<head>
		<meta charset="UTF-8" />
		
		<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
		<title>股票小工具title>
	head>
	<body>
		<div id="app">
			
			<el-row :gutter="10" style="margin-top: 20px;">
				<el-card class="box-card">
					<div slot="header" class="clearfix">
						<span>K线图span>
					div>
					<el-row>
						<el-col>
							<el-form label-width="80px" :inline="true" :model="stockForm">
								<el-form-item label="股票代码:">
									<el-input v-model="stockForm.code" placeholder="例如:000001.SZ" clearable />
								el-form-item>
								<el-form-item>
									<el-button type="primary" v-if="stockForm.code"
										@click="stockFormSubmit('stockForm')">确定el-button>
									<el-tooltip class="item" v-else effect="dark" content="请填写股票代码!">
										<el-button type="danger">确定el-button>
									el-tooltip>
								el-form-item>
								<el-form-item>
									
										[{stockName+forcastMsg}]
									el-tag>
									<el-tag v-else type="success" effect="dark">
										[{stockName+forcastMsg}]
									el-tag>
								el-form-item>
							el-form>
						el-col>
					el-row>
					<el-row>
						<div id="main" style="width:1000px;height:500px;">div>
					el-row>
				el-card>
			el-row>
		div>
	body>
	
	<script src="../static/js/echarts.min.js">script>
	
	<script src="https://unpkg.com/[email protected]/dist/vue.min.js">script>
	
	
	<script src="https://unpkg.com/element-ui/lib/index.js">script>
	
	<script src="https://unpkg.com/axios/dist/axios.min.js">script>
	<script src="https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js">script>
	<script>
		const upColor = "#ec0000"
		const upBorderColor = "#8A0000"
		const downColor = "#00da3c"
		const downBorderColor = "#008F28"

		var nv = new Vue({
			el: '#app',
			delimiters: ['[{', '}]'],
			data: function() {
				return {
					// 表单数据
					stockForm: {
						code: '000001.SZ',
					},
					// 图表数据
					kChartdata: null,
					// 预测信息
					forcastMsg: "预测信息",
					// 股票名
					stockName: "",
				}
			},
			created:function(){
				this.stockFormSubmit()
			},
			methods: {
				// 获取输入的股票代码数据
				async stockFormSubmit(stockForm) {
					const {
						data: res
					} = await this.$http.post("http://127.0.0.1:12345/stock", this.stockForm)
					if (res.status != 200) {
						this.$message({
							showClose: true,
							message: res.msg,
							center: true,
							type: 'error'
						})
					} else {
						this.$message({
							showClose: true,
							message: res.msg,
							center: true,
							type: 'success'
						})
						// 重新赋值新的股票数据
						var myChart = echarts.init(document.getElementById('main'))
						this.kChartdata = res.stock_data
						this.forcastMsg = this.kChartdata.forcast
						this.stockName = this.kChartdata.name
						var option = {
							// 图表标题配置
							title: {
								text: this.stockName + '近一年K线图'
							},
							// 提示框配置
							tooltip: {
								// 是否显示提示框
								show: true,
								// 触发类型,axis 移动到坐标轴就触发
								trigger: "axis",
								// 坐标轴上提示点设置
								axisPointer: {
									type: "cross"
								}
							},
							// 图例配置
							legend: {
								data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
							},
							// X 轴配置项
							xAxis: {
								type: "category",
								data: this.kChartdata.stock_trade_days,
								boundaryGap: false,
								axisLine: {
									onZero: false
								},
								splitLine: {
									show: true
								},
								min: "dataMin",
								max: "dataMax"
							},
							// y 轴配置项
							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: this.kChartdata.stock_trade_data,
									// K 线图的图形样式
									itemStyle: {
										// 阳线 图形的颜色
										color: upColor,
										// 阴线 图形的颜色。
										color0: downColor,
										// 阳线 图形的描边颜色
										borderColor: upBorderColor,
										// 阴线 图形的描边颜色
										borderVolor0: downBorderColor
									},
									// 标记点配置
									markPoint: {
										// 标注的文本
										label: {
											// 标签内容格式器,支持字符串模板和回调函数两种形式,字符串模板与回调函数返回的字符串均支持用 \n 换行。
											// 参数 params 是 formatter 需要的单个数据集, value是传入的数据值。
											formatter: function(param) {
												return param != null ? Math.round(param.value) + "" :
													"";
											}
										},
										// 标注的数据数组。每个数组项是一个对象
										data: [
											// 特殊的标注类型,用于标注最大值最小值等
											{
												name: "highest value",
												type: "max",
												// 在使用 type 时有效,用于指定在哪个维度上指定最大值最小值。
												valueDim: "highest",
											},
											{
												name: 'lowest value',
												type: 'min',
												valueDim: 'lowest'
											},
											{
												name: 'average value on close',
												type: 'average',
												valueDim: 'close'
											}
										],
										// 提示框浮层内容格式器,支持字符串模板和回调函数两种形式。
										tooltip: {
											// params 是 formatter 需要的数据集
											formatter: function(param) {
												// name数据名,类目名,data传入的原始数据项
												return param.name + "
"
+ (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: this.kChartdata.MA5, smooth: true, lineStyle: { opacity: 0.5 } }, { name: "MA10", type: "line", data: this.kChartdata.MA10, smooth: true, lineStyle: { opacity: 0.5 } }, { name: "MA20", type: "line", data: this.kChartdata.MA20, smooth: true, lineStyle: { opacity: 0.5 } }, { name: "MA30", type: "line", data: this.kChartdata.MA30, smooth: true, lineStyle: { opacity: 0.5 } }, ] } //使用刚指定的配置项和数据显示图像 myChart.setOption(option) } }, } });
script> html>

后端接口

使用flask搭建的微应用,接口介绍:

  • stock:获取输入的股票数据,返回交易数据、MA数据以及预测数据
  • index:获取主界面
import pandas as pd
from flask import Flask, render_template, request
from flask import jsonify
from flask_cors import CORS
import webbrowser
from datetime import datetime, timedelta
import tushare as ts

ts_token = "填写在tushare获取的token"
ts.set_token(ts_token)
pro = ts.pro_api()

app = Flask(__name__)
CORS(app, supports_credentials=True)


def get_stock(code):
    # 获取股票数据
    end_date = datetime.now().strftime("%Y%m%d")
    start_date = datetime.strftime(datetime.now() - timedelta(365), "%Y%m%d")
    df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date).sort_values(by="trade_date")
    try:
        data = pro.query('stock_basic', exchange='', list_status='L', fields='ts_code,name')
        data.to_excel("./static/excel/stock_basic_info.xlsx", index=False)
        name = data[data["ts_code"] == code]["name"].values[0]
    except Exception as e:
        data = pd.read_excel("./static/excel/stock_basic_info.xlsx")
        name = data[data["ts_code"] == code]["name"].values[0]
    if df.empty:
        return None
    df["MA5"] = df["close"].rolling(4).mean().map(lambda x: round(x, 3))
    df["f1"] = df["MA5"] - df["close"]
    forcast_msg = f"预测 {end_date} 会跌!"
    if sum(df.tail(7)["f1"] > 0) > 4:
        forcast_msg = f"预测 {end_date} 会涨!"
    result = {
        "stock_trade_days": df["trade_date"].values.tolist(),
        "stock_trade_data": df[["open", "close", "low", "high"]].values.tolist(),
        "MA5": df["close"].rolling(4).mean().map(lambda x: round(x, 3)).fillna("-").tolist(),
        "MA10": df["close"].rolling(9).mean().map(lambda x: round(x, 3)).fillna("-").tolist(),
        "MA20": df["close"].rolling(19).mean().map(lambda x: round(x, 3)).fillna("-").tolist(),
        "MA30": df["close"].rolling(29).mean().map(lambda x: round(x, 3)).fillna("-").tolist(),
        "forcast": forcast_msg,
        "name": name
    }
    return result


@app.route("/")
def index():
    return render_template('index.html')


@app.route("/stock", methods=["GET", "POST"])
def stock():
    # 获取提交的股票代码
    rq_json = request.json
    stock_code = rq_json["code"]
    # 获取股票数据,数据解释:时间,开盘价,收盘价,最低价,最高价
    get_stock_result = get_stock(stock_code)
    if get_stock_result:
        result = {
            "status": 200,
            "stock_data": get_stock_result,
            "msg": f"获取{stock_code}股票数据成功!"
        }
        return jsonify(result)
    else:
        result = {
            "status": 400,
            "stock_data": None,
            "msg": f"获取{stock_code}股票数据失败!"
        }
        return jsonify(result)


if __name__ == '__main__':
    webbrowser.open("http://127.0.0.1:12345/")
    app.run(debug=False, port=12345)

运行后就可以在浏览器中看到,感兴趣的朋友可以试试!

源码地址

链接:https://pan.baidu.com/s/1pkAZDi3jLQoh8vifHaj0nA?pwd=vd3s

你可能感兴趣的:(数据分析,数据可视化,echarts,python,开发语言,echarts,flask,pycharm,数据挖掘,数据分析)