对码当歌,猿生几何?

Python秒绘交互式股票K线图!这就是Python的优势!

摘要:可视化分析是做金融量化必备的技能,今天给大家分享一篇文章,使用Python,在PyQt5中借助PyQtGtaph绘制一个带有十字光标的股票价格走势K线图

欢迎大家加入小编创建的Python行业交流群,有大牛答疑,有资源共享,有企业招人!是一个非常不错的交流基地!群号:683380553获取。

2019年开年以来,A股行情不断走高,去年跌跌不休的日子总算看着像是过去了。不少股民纷纷解套走人,不少外场人士则被新闻媒体渲染的“A股大牛市来了”刺激得兴奋不已,生怕错过这一轮造富时机,恨不得马上卖房入场。

image.png

(截图来自于同花顺实时行情)

俗话说得好,股市秘笈千万条,看懂K线第一条。想研究股票,似乎总要研究K线。但是今天我们不研究K线,看K线那是股中人士的活儿。

对于刚刚解套的我来说,还是画画K线比较自在。

今天,我们将使用Python,在PyQt5中借助PyQtGtaph绘制一个带有十字光标的股票历史走势K线图。

一、创建图形界面窗口骨架

首先,我们来创建一个基础的图形界面。里面包含了:

一个文本输入框,用于输入股票代码;

一个下拉选择框,用于选择时间段;

一个按钮,用于点击查询数据和生成K线图;

一个空白图形,用于放置K线图;

通过如下代码进行创建:

# 主窗口类

class MainUi(QtWidgets.QMainWindow):

   def __init__(self):

       super().__init__()

       self.setWindowTitle("州的先生zmister.com A股股票历史走势K线图")

       self.main_widget = QtWidgets.QWidget() # 创建一个主部件

       self.main_layout = QtWidgets.QGridLayout() # 创建一个网格布局

       self.main_widget.setLayout(self.main_layout) # 设置主部件的布局为网格

       self.setCentralWidget(self.main_widget) # 设置窗口默认部件

       self.stock_code = QtWidgets.QLineEdit() # 创建一个文本输入框部件

       self.option_sel = QtWidgets.QComboBox() # 创建一个下拉框部件

       self.option_sel.addItem("近7天")

       self.option_sel.addItem("近30天")

       self.option_sel.addItem("近60天")

       self.option_sel.addItem("近180天")

       self.option_sel.addItem("近360天")

       self.que_btn = QtWidgets.QPushButton("查询") # 创建一个按钮部件

       self.k_widget = QtWidgets.QWidget() # 实例化一个widget部件作为K线图部件

       self.k_layout = QtWidgets.QGridLayout() # 实例化一个网格布局层

       self.k_widget.setLayout(self.k_layout) # 设置K线图部件的布局层

       self.k_plt = pg.PlotWidget() # 实例化一个绘图部件

       self.k_layout.addWidget(self.k_plt) # 添加绘图部件到K线图部件的网格布局层

       # 将上述部件添加到布局层中

       self.main_layout.addWidget(self.stock_code,0,0,1,1)

       self.main_layout.addWidget(self.option_sel,0,1,1,1)

       self.main_layout.addWidget(self.que_btn,0,2,1,1)

       self.main_layout.addWidget(self.k_widget,1,0,3,3)

运行程序,我们可以得到一个如下图所示的图形界面窗口:

image.png

image.png

接下来,我们创建一个K线图的图形绘制类,通过PyQt和PyQtGraph的绘图组件绘制K线图。

二、创建K线图绘制类

接着创建一个名为CandlestickItem()的类,其继承于pyqtgraph的GraphicsObject类。

通过QPicture和QPainter进行绘图操作实现K线图的绘制。具体代码如下所示:

# K线图绘制类

class CandlestickItem(pg.GraphicsObject):

   # 州的先生zmister.com

   def __init__(self, data):

       pg.GraphicsObject.__init__(self)

       self.data = data  # data里面必须有以下字段: 时间, 开盘价, 收盘价, 最低价, 最高价

       self.generatePicture()

   def generatePicture(self):

       self.picture = QtGui.QPicture() # 实例化一个绘图设备

       p = QtGui.QPainter(self.picture) # 在picture上实例化QPainter用于绘图

       p.setPen(pg.mkPen('w')) # 设置画笔颜色

       w = (self.data[1][0] - self.data[0][0]) / 3.

       for (t, open, close, min, max) in self.data:

           print(t, open, close, min, max)

           p.drawLine(QtCore.QPointF(t, min), QtCore.QPointF(t, max)) # 绘制线条

           if open > close: # 开盘价大于收盘价

               p.setBrush(pg.mkBrush('g')) # 设置画刷颜色为绿

           else:

               p.setBrush(pg.mkBrush('r')) # 设置画刷颜色为红

           p.drawRect(QtCore.QRectF(t - w, open, w * 2, close - open)) # 绘制箱子

       p.end()

   def paint(self, p, *args):

       p.drawPicture(0, 0, self.picture)

   def boundingRect(self):

       return QtCore.QRectF(self.picture.boundingRect())

这个类用于生成K线图的图形,其接收一个数组其中包含时间、开盘价、收盘价、最低价和最高价的列表,我们只需要将其添加到PyQtGraph的绘图方法中,就可以生成具体的K线图图形。

下面,我们来完善具体的K线图绘制方法。

三、生成K线图

在创建好K线图绘制类之后,我们来实现K线图的具体绘制工作。我们的数据来源于tushare这个第三方库提供的A股个股历史数据。获取数据之后,我们对数据进行加工,处理成CandlestickItem()类接受的数据格式,传入给CandlestickItem()。

在得到K线图之后,我们将其添加到之前实例化好的PlotWidget()部件self.k_plt中,并对图形添加设置其他属性,其代码如下所示:

   def plot_k_line(self,code=None,start=None,end=None):

       self.data = ts.get_hist_data(code=code, start=start, end=end).sort_index()

       y_min = self.data['low'].min()

       y_max = self.data['high'].max()

       data_list = []

       d = 0

       for dates, row in self.data.iterrows():

           # 将时间转换为数字

           date_time = datetime.datetime.strptime(dates, '%Y-%m-%d')

           # t = date2num(date_time)

           open, high, close, low = row[:4]

           datas = (d, open, close, low, high)

           data_list.append(datas)

           print(datas)

           d += 1

       self.axis_dict = dict(enumerate(self.data.index))

       # 州的先生 zmister.com

       axis_1 = [(i, list(self.data.index)[i]) for i in range(0, len(self.data.index), 3)]  # 获取日期值

       axis_2 = [(i, list(self.data.index)[i]) for i in range(0, len(self.data.index), 5)]

       axis_3 = [(i, list(self.data.index)[i]) for i in range(0, len(self.data.index), 8)]

       axis_4 = [(i, list(self.data.index)[i]) for i in range(0, len(self.data.index), 10)]

       axis_5 = [(i, list(self.data.index)[i]) for i in range(0, len(self.data.index), 30)]

       stringaxis = pg.AxisItem(orientation='bottom')  # 创建一个刻度项

       stringaxis.setTicks([axis_5, axis_4, axis_3, axis_2, axis_1, self.axis_dict.items()])  # 设置X轴刻度值

       self.k_plt.getAxis("bottom").setTicks([axis_5, axis_4, axis_3, axis_2, axis_1, self.axis_dict.items()])

       self.k_plt.plotItem.clear() # 清空绘图部件中的项

       item = CandlestickItem(data_list)  # 生成蜡烛图数据

       self.k_plt.addItem(item, )  # 在绘图部件中添加蜡烛图项目

       self.k_plt.showGrid(x=True, y=True)  # 设置绘图部件显示网格线

       self.k_plt.setYRange(y_min,y_max)

       self.k_plt.setLabel(axis='left', text='指数')  # 设置Y轴标签

       self.k_plt.setLabel(axis='bottom', text='日期')  # 设置X轴标签

       self.label = pg.TextItem()  # 创建一个文本项

       self.k_plt.addItem(self.label)  # 在图形部件中添加文本项

       self.vLine = pg.InfiniteLine(angle=90, movable=False, )  # 创建一个垂直线条

       self.hLine = pg.InfiniteLine(angle=0, movable=False, )  # 创建一个水平线条

       self.k_plt.addItem(self.vLine, ignoreBounds=True)  # 在图形部件中添加垂直线条

       self.k_plt.addItem(self.hLine, ignoreBounds=True)  # 在图形部件中添加水平线条

这个方法将是我们点击【查询】按钮,对点击信号进行处理时需要调用的方法,它是在图形界面窗口中显示K线图的关键。

我们继续创建一个方法,用来调用plotkline()方法,并将其连接到【查询】按钮的点击信号上:

   # 查询按钮信号槽

   def query_slot(self):

       try:

           self.que_btn.setEnabled(False)

           self.que_btn.setText("查询中…")

           code = self.stock_code.text()

           date_sel = self.option_sel.currentText()[1:-1]

           start_date = datetime.datetime.today()-datetime.timedelta(days=int(date_sel)+1)

           start_date_str = datetime.datetime.strftime(start_date,"%Y-%m-%d")

           end_date = datetime.datetime.today()-datetime.timedelta(days=1)

           end_date_str = datetime.datetime.strftime(end_date,"%Y-%m-%d")

           print(code,start_date_str,end_date_str)

           self.plot_k_line(code=code,start=start_date_str,end=end_date_str)

           self.que_btn.setEnabled(True)

           self.que_btn.setText("查询")

       except Exception as e:

           print(traceback.print_exc())

【查询】按钮点击信号绑定:

self.que_btn.clicked.connect(self.query_slot) # 绑定按钮点击信号

这样,我们运行代码,就可以通过输入股票代码和选择时间间隔来查看对应股票的动态历史K线图了,如下动图所示:

四、绘制十字光标

上面的图形界面程序生成了股票的K线图,但是我们却不能方便地查看到具体一天的价格变动,一个十字光标的鼠标指示必需的,我们接着来实现它。

   # 响应鼠标移动绘制十字光标

   def print_slot(self, event=None):

       if event is None:

           print("事件为空")

       else:

           pos = event[0]  # 获取事件的鼠标位置

           try:

               # 如果鼠标位置在绘图部件中

               if self.k_plt.sceneBoundingRect().contains(pos):

                   mousePoint = self.k_plt.plotItem.vb.mapSceneToView(pos)  # 转换鼠标坐标

                   index = int(mousePoint.x())  # 鼠标所处的X轴坐标

                   pos_y = int(mousePoint.y())  # 鼠标所处的Y轴坐标

                   if -1 < index < len(self.data.index):

                       # 在label中写入HTML

                       self.label.setHtml(

                           "<p style='color:white'><strong>日期:{0}</strong></p><p style='color:white'>开盘:{1}</p><p style='color:white'>收盘:{2}</p><p style='color:white'>最高价:<span style='color:red;'>{3}</span></p><p style='color:white'>最低价:<span style='color:green;'>{4}</span></p>".format(

                               self.axis_dict[index], self.data['open'][index], self.data['close'][index],

                               self.data['high'][index], self.data['low'][index]))

                       self.label.setPos(mousePoint.x(), mousePoint.y())  # 设置label的位置

                   # 设置垂直线条和水平线条的位置组成十字光标

                   self.vLine.setPos(mousePoint.x())

                   self.hLine.setPos(mousePoint.y())

           except Exception as e:

               print(traceback.print_exc())

这个方法将为我们的图形实时绘制生成一个十字光标和一个显示鼠标所在坐标日期的数据指标。

我们需要将其连接到self.k_plt这个图形部件的信号事件上,使得鼠标移动时可以实时响应:

# 州的先生 https://zmister.com

self.move_slot = pg.SignalProxy(self.k_plt.scene().sigMouseMoved, rateLimit=60, slot=self.print_slot)

现在运行代码,我们就可以看到生成的K线图有十字光标实时显示鼠标所在坐标日期的股票数据了。如下动图所示: