代码拉取完成,页面将自动刷新
# K线图样式 csv+mplfinance https://blog.csdn.net/qq_41437512/article/details/105319421
# tushare+mplfinance——最小案例 https://blog.csdn.net/ooobenooo/article/details/107754092
# datatime——时间与字符串处理 https://www.cnblogs.com/chenqionghe/p/12235277.html
# mplfinance官网文档优秀翻译 https://blog.csdn.net/wuwei_201/article/details/105781844
# dataframe基础操作 https://www.jianshu.com/p/4ff1d2b23ab3
from datetime import datetime, timedelta, date
import matplotlib as mpl
import mplfinance as mpf
import pandas as pd
import tushare as ts
api = None # tushare 获取数据的接口对象
data = None # 用于保存股票数据
data_insert = None # 用于保存预测的那一条股票数据
sCode = None # 用于保存股票代码
g_start_date = None # 用于保存查询到的数据的开始时间
g_end_date = None # 用于数据的结束时间
times = None # 用于保存数据的时间段的字符串
tk = {} # 用于保存预测值的 'open' 'close' 'high' 'low' 'volume'的字典对象
# 从命令行读取股票代码,并保存至全局变量 sCode
def get_sCode():
global sCode # 显式引用全局变量 sCode
# 从命令行读取股票代码,并保存至全局变量 sCode
sCode = input("请按格式输入股票代码:%s\n" % ('300059.SZ'))
# 如果是正常的股票代码,应该是9个字符
# 当然,我们无法保证9个字符的输入就一定是正确的股票代码,不过没关系,可以不管它
if len(sCode) != 9: # 如果输入不是9个字符
# 就把 sCode 设置为写死的300059.SZ
print("您输入了不正确的股票代码 >>> %s\n已修改股票代码为默认值 >>> 300059.SZ" % sCode)
sCode = '300059.SZ'
# 设置时间。之后要按着这个时间来去查询股票数据
# 设置参数 next,next 意为是否取下一天,next 默认值为 False
# 若 next=True 则 g_end_date 取下一个工作日
# 若 next=False 则 g_end_date 取今天(而不是下一个工作日)
def set_time(next=False):
# 显式引用全局变量 g_start_date g_end_date times
global g_start_date
global g_end_date
global times
# 字符串::开始时间 = (当前时间 - 270天).格式化为"年月日"
g_start_date = (datetime.now() - timedelta(270)).strftime('%Y%m%d')
# 若 next=True 则 字符串::结束时间 取下一个工作日
# 若 next=False 则 字符串::结束时间 取今天(而不是下一个工作日)
g_end_date = next_workday().strftime('%Y%m%d') \
if next else datetime.now().strftime('%Y%m%d')
# 字符串::times =
# (按格式解析时间字符串(字符串::开始时间,格式字符串::"年月日"))转换成字符串.取[0,-9)
# + ' ~ ' +
# (按格式解析时间字符串(字符串::结束时间,格式字符串::"年月日"))转换成字符串.取[0,-9)
times = str(datetime.strptime(g_start_date, '%Y%m%d'))[:-9] + \
" ~ " + str(datetime.strptime(g_end_date, '%Y%m%d'))[:-9]
# tushare 初始化设置
def init_tushare():
# 显式引用全局变量 api
global api
# tushare 用token设置初始化
ts.set_token('dd8ab61a5210d4fa6ed707fd36c2db22986e7d3909e28b71e58f2f99')
# 初始化完成以后,就可以用 .pro_api() 来拿到一个对象
# 将这个对象赋值给 api ,留着以后用
api = ts.pro_api()
# 获取数据
def get_data():
# 需要先用 api.query(...) 查询数据,然后把查询到的数据转换成可以绘制的格式
# 显式引用全局变量 data sCode times g_start_date g_end_date
global data
global sCode
global times
global g_start_date
global g_end_date
# 设置时间为180个交易日至今天
set_time(next=False)
# Columns: [ts_code, trade_date, open, high, low, close, pre_close, change, pct_chg, vol, amount]
# 查询数据并保存到 DataFrame::df
# api.query()用于查询股票数据,有多个配置参数
# api_name='daily' 以天为精度来查询股票数据(我的账号的积分只能支持这个精度的)
# ts_code=sCode 设置查询目标——股票代码为全局变量股票代码
# start_date=g_start_date 设置查询目标——开始时间为全局变量开始时间
# end_date=g_end_date 设置查询目标——结束时间为全局变量结束时间
df = api.query(api_name='daily',
ts_code=sCode,
start_date=g_start_date,
end_date=g_end_date)[::-1]
# [::-1]实际上是简单的转换,倒序取得数据的开头从开始到最后一行
# 数据内容并没有变化(还是全部),但是数据的顺序变成了原先的倒序。
# 如果查到数据以后发现数据为空,那么证明两件事情:
# 第一 不是网络错误或是其他错误,否则在执行 api.query(...) 就会直接报错程序中止
# 第二 确实查到了数据,不过数据为空,那就只能是股票代码有问题
if df.empty:
# 打印提示信息 就说股票代码在某时段查询失败
print("\n\nERR!!! 股票:%s 在 %s 的数据查询失败,请仔细检查股票代码\n\n" % (sCode, times))
# 同时抛出异常,就说股票代码在某时段查询失败
raise Exception("股票:%s 在 %s 的数据查询失败,请仔细检查股票代码" % (sCode, times))
else:
# 如果一切正常,那么可以打印出本次查询到数据的基础信息
print("股票:%s 在 %s 的数据 已查得\n数据共有 %d 行\n" %
(sCode, times, df.size / 11))
# df.size 是 df 里面共有多少数据
# 11 是 df 共有 11 列,所以每行有 11 个数据
# 因此 df.size / 11 就可以得到df共有几行数据
# 为df创建一个新的列 date
# 这一列的数据就是 df 的 trade_date 这一列的每一项作为参数传递给lambda函数后计算的结果
# 这行 lambda 函数将时间字符串按格式解析成 datetime 对象
df['date'] = df['trade_date'].apply(
lambda x: datetime.strptime(x, '%Y%m%d'))
# 然后创建一个新的 DataFrame 对象 data
# 对这个data对象进行格式整理,以便能作为绘图数据被调用
# 从 df 查数据 df.loc[哪行,哪列]
# df.loc[:,[...]] 相当于 df.loc[所有行,[这些列]]
# 得到的数据传递给 data(一个新的 DataFrame 变量)
data = df.loc[:, ['date', 'open', 'close', 'high', 'low', 'vol']]
# 格式要求:vol 这一列的列名改为 volume
data = data.rename(columns={"vol": "volume"})
# 格式要求,将 date 这一列设置为索引列
data.set_index('date', inplace=True)
# 格式要求,将索引列的名字更改为 Date
data.index.name = "Date"
# 格式要求,将索引列的数据的数据类型更改为 DatetimeIndex
data.index = pd.DatetimeIndex(data.index)
# 最最重要的是,索引列index::Date 会被作为绘图时的X轴数据
# data.axes # 可打印 data 的索引列信息
# data.shape # 可打印 data 的 shape
# print(df)
# print(data)
# 把 data 数据绘制成股票趋势图
# 参数castmode 设置是否是预测图 默认值 False
# 若 castmode=True,则绘制的是预测图
# 若 castmode=False,则绘制的是真实数据的图
def draw(castmode=False):
# show_nontrading:是否显示非交易日,默认False
global data
global sCode
global times
# 设置全局变量 time
# 根据参数castmode 来设置时间的结束日期是今天还是下一个工作日
# 详情请见 set_time 函数的注释部分
set_time(next=castmode)
# print(mpl.rcParams) # 打印参数列表,开发程序的时候用的。
# 尝试设置标题能正常显示中文,失败了
# mpl.rcParams["font.family"] = 'Arial Unicode MS' # 据说这是mac上设置中文 然并卵
# 下面是对mplfinance的调用,具体的代码注释请见上文的 第三方依赖库的介绍 => mplfinance
s = mpf.make_mpf_style(base_mpf_style='nightclouds', marketcolors=mpf.make_marketcolors(up='r', down='g', edge='i', wick='i'), mavcolors=[
'#ffffff', '#f7d652', '#eb60f9', '#adadad', '#0647ef'])
mpl.rcParams['lines.linewidth'] = 0.1
mpl.rcParams['figure.dpi'] = 2000
mpl.rcParams['savefig.dpi'] = 2000
tableName = "%s\n%s" % (sCode, times)
# figpath = ('/Users/wangweijie/Desktop/股票预测走势绘制/Forcast:%s:%s' % (sCode, times) + '.png') \
# if castmode else'/Users/wangweijie/Desktop/股票预测走势绘制/%s:%s' % (sCode, times) + '.png'
config = dict(
type='candle', style=s, mav=(
5, 10, 20, 60, 120), figratio=(5, 2), title=tableName, figscale=1.4, datetime_format='%m/%d', xrotation=0
)
print("股票:%s 在 %s 的数据 已读取 \n现有数据共有 %d 行\n" % (sCode, times, data.size / 5))
print(data.head(3))
print(data.tail(3))
# mpf.plot(data, **config, savefig=figpath)
if castmode:
# 如果当前模式是预测模式,那就渲染并弹出图片窗口
mpf.plot(data, **config)
else:
# 如果当前模式是原图模式,那就询问是否要看原始数据图
print("\n\n是否希望看到 原始数据 的走势图")
print("Yes | yes | Y | y >>> 是")
print("其他内容(例如回车) >>> 不,我选择不看原始数据图")
key = input("请根据提示输入: ")
if key == "Yes" or key == "yes" or key == "Y" or key == "y":
print("将弹出原始数据的走势图")
print("按q关闭图片窗口")
print("在图片窗口被关闭前,将无法开始预测")
mpf.plot(data, **config)
else:
print("跳过展示 原始数据 的走势图")
# 获取下一个工作日
# 函数返回值是一个 时间对象 就是下一个工作日
def next_workday():
# 首先得到 今天 这个时间对象
today = datetime.now()
if (today.weekday() == 4):
# 如果今天是周五,那下一个工作日是往后数三天
nextwork_day = date.today() + timedelta(days=3)
elif (today.weekday() == 5):
# 如果今天是周六,那下一个工作日是往后数两天
nextwork_day = date.today() + timedelta(days=2)
else:
# 否则,那下一个工作日是往后数一天
nextwork_day = date.today() + timedelta(days=1)
# 返回计算出的下一个工作日
return nextwork_day
# 获取预测值数据并保存到全局变量 tk 这个字典里
def get_tk():
global tk # 显式引用全局变量tk
# 设置死循环,不输入正确不让退出
while True:
try:
print("请按提示仔细输入")
tk['open'] = float(input("请输入:开盘价\n"))
tk['close'] = float(input("请输入:收盘价\n"))
tk['high'] = float(input("请输入:当日最高价\n"))
tk['low'] = float(input("请输入:当日最低价\n"))
except ValueError:
# input(...) 函数的返回值就是用户在命令行中的输入,是字符串
# 需要用 float(...) 将这个字符串转换为浮点数再赋值到字典 tk
# 如果输入的数字不能解析成浮点数,就会触发 ValueError 这个异常
# 那么打印"输入值未满足格式要求:自然数或小数"这句话提醒用户检查输入的内容
# 然后开始新一轮循环
print("输入值未满足格式要求:自然数或小数")
pass # 如果输入不能转换成浮点数,就重新来触发输入
else:
# 如果没有触发数据转换为数字的异常,也要保证数据满足基本的大小值关系
if not (tk['high'] >= tk['open'] and tk['high'] >= tk['close'] and tk['low'] <= tk['open'] and tk['low'] <= tk['close'] and tk['low'] <= tk['high']):
print("输入值未满足大小关系:最低价 <= (开盘价,收盘价) <= 最高价")
pass # 大小关系不满足,则重新输入
else:
print("成功设置:开盘价、收盘价、当日最高价、当日最低价 ")
print(tk)
break # 一切顺利,完成输入,退出死循环
# 启动读入预测数据 然后修改数据 并重新渲染走势图窗口
def forecast_data():
# 显式引用全局变量 tk 和 data 和 data_insert
global tk
global data
global data_insert
# 从命令行读取预测的四个值
get_tk()
# 创建 data_insert 为一个新的DataFrame对象,里面只有一行数据,就是预测的那一行
data_insert = pd.DataFrame(
# 设置它的索引是 下一个工作日
index=[next_workday()],
data={
# 设置 data 的各个栏目为相应的值
'open': [tk['open']],
'close': [tk['close']],
'high': [tk['high']],
'low': [tk['low']],
# 成交量数据不重要 干脆置为零
'volume': [0]
})
# append并不修改原data,只是返回一个新的DataFrame
# 所以,需要进行赋值,将返回值赋给data
data = data.append(data_insert, ignore_index=False)
# 因为新添加的这一项的index是date而不是datetimeIndex,所以整个data的index就变成object了
# 用下面这行来重新转换为datatimeindex
data.index = pd.DatetimeIndex(data.index)
# data.axes 可以查询现在的index状况
# 调用我们的绘图函数,castmode=True 即选择 预测图模式
draw(castmode=True)
# 之前导入的包,定义的全局变量,定义好的函数,都可以在此处调用
# 之前定义的函数内的语句,是不会自动执行的,需要在这里调用函数,他们才会执行
if __name__ == "__main__":
get_sCode() # 从命令行读取股票代码到全局变量 sCode
set_time() # 默认值 next 为 false,即,并不会设置为下一个交易日
init_tushare() # tushare 为了能读取数据,需要初始化
get_data() # 根据读取今天往前数270天的数据,约为180个交易日
draw() # 默认值 castmode 为 false,即,并不会绘制和或保存预测数据的图片
while True:
print("\n\n是否要输入新的预测值")
print("Yes | yes | Y | y >>> 是")
print("No | no | N | n >>> 不,我选择退出程序")
key = input("请根据提示输入: ")
if key == "Yes" or key == "yes" or key == "Y" or key == "y":
print("开始新的一次预测")
print("在预测图渲染图片窗口弹出后,可以按q关闭图片窗口")
print("在图片窗口被关闭前,将无法开始下一轮预测")
forecast_data() # 触发预测数据
elif key == "No" or key == "no" or key == "N" or key == "n":
print("退出预测 程序中止")
break
else:
print("输入值不合法 <<< %s 请按提示重新输入" % key)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。