量化百科

【开源一个用于回测的Python交互K线工具】之【一】K线核心功能

由polll创建,最终由polll 被浏览 77 用户

动机

个人量化研究,用vn.py回测和研究策略。

发现最痛苦的事情就是写完一个策略后,根本没法方便地检查自己的交易逻辑。每次打印日志之后,翻日志再找其他K线工具来校对,这个过程简直泪流满面。

![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='375' height='147'></svg>)

在无数次面对日志抓瞎以后,决心寻找一个好用的Python K线工具,于是在知乎搜索 Python K线

哦,不错,有很多不错的Python K线案例嘛!

但是!!

刨去静态图片啊,上面的动态交互工具,都没办法让我方便地把策略回测的结果放进去。

如果能按一下键盘就可以在开平仓标记位置biu,biu跳转就更好了。

看来自己手撸一个交互K线是免不了的

![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='192'></svg>)

结合商业软件的K线,简单列一下需求:

  1. 屏幕K线数少的时候,反应要快
  2. 鼠标滚轮缩放,键盘缩放跳转
  3. 十字光标 显示K线详细信息
  4. 缩放自适应Y轴坐标
  5. 策略回测运行中产生的指标可以放到K线图中
  6. 买卖开平仓位置有箭头标记,并且通过键盘可以在标记之间跳转

成果

废话不说了,先来看成果

![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1915' height='1016'></svg>)

基础K线模块

![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1888' height='960'></svg>)

回测结果K线展示

  • 界面风格抄袭了市面上看到的商业软件
  • 界面缩放,十字光标移动顺畅
  • 回测完以后可以直接把开平仓标记和策略的技术指标显示到界面
  • 键盘PgUp和PgDn可以在开平仓点自由切换了

K线图形对象部分代码

########################################################################
# K线图形对象
########################################################################
class CandlestickItem(pg.GraphicsObject):
    """K线图形对象"""

    # 初始化
    #----------------------------------------------------------------------
    def __init__(self, data):
        """初始化"""
        pg.GraphicsObject.__init__(self)
        # 数据格式: [ (time, open, close, low, high),...]
        self.data = data
        # 只重画部分图形,大大提高界面更新速度
        self.rect = None
        self.picture = None
        self.setFlag(self.ItemUsesExtendedStyleOption)
        # 画笔和画刷
        w = 0.4
        self.offset   = 0
        self.low      = 0
        self.high     = 1
        self.picture  = QtGui.QPicture()
        self.pictures = []
        self.bPen     = pg.mkPen(color=(0, 240, 240, 255), width=w*2)
        self.bBrush   = pg.mkBrush((0, 240, 240, 255))
        self.rPen     = pg.mkPen(color=(255, 60, 60, 255), width=w*2)
        self.rBrush   = pg.mkBrush((255, 60, 60, 255))
        self.rBrush.setStyle(Qt.NoBrush)
        # 刷新K线
        self.generatePicture(self.data)          


    # 画K线
    #----------------------------------------------------------------------
    def generatePicture(self,data=None,redraw=False):
        """重新生成图形对象"""
        # 重画或者只更新最后一个K线
        if redraw:
            self.pictures = []
        elif self.pictures:
            self.pictures.pop()
        w = 0.4
        bPen   = self.bPen
        bBrush = self.bBrush
        rPen   = self.rPen
        rBrush = self.rBrush
        low,high = (data[0]['low'],data[0]['high']) if len(data)>0 else (0,1)
        for (t, open0, close0, low0, high0) in data:
            if t >= len(self.pictures):
                picture = QtGui.QPicture()
                p = QtGui.QPainter(picture)
                low,high = (min(low,low0),max(high,high0))
                # 下跌蓝色(实心), 上涨红色(空心)
                pen,brush,pmin,pmax = (bPen,bBrush,close0,open0)\
                    if open0 > close0 else (rPen,rBrush,open0,close0)
                p.setPen(pen)  
                p.setBrush(brush)
                # 画K线方块和上下影线
                if open0 == close0:
                    p.drawLine(QtCore.QPointF(t-w,open0), QtCore.QPointF(t+w, close0))
                else:
                    p.drawRect(QtCore.QRectF(t-w, open0, w*2, close0-open0))
                if pmin  > low0:
                    p.drawLine(QtCore.QPointF(t,low0), QtCore.QPointF(t, pmin))
                if high0 > pmax:
                    p.drawLine(QtCore.QPointF(t,pmax), QtCore.QPointF(t, high0))
                p.end()
                self.pictures.append(picture)
                self.ranges.append((low0,high0))
        self.low,self.high = low,high

    # 手动重画
    #----------------------------------------------------------------------
    def update(self):
        if not self.scene() is None:
            self.scene().update()

    # 自动重画
    #----------------------------------------------------------------------
    def paint(self, painter, opt, w):
        rect = opt.exposedRect
        xmin,xmax = (max(0,int(rect.left())),min(int(len(self.pictures)),int(rect.right())))
        if not self.rect == (rect.left(),rect.right()) or self.picture is None:
            self.rect = (rect.left(),rect.right())
            self.picture = self.createPic(xmin,xmax)
            self.picture.play(painter)
        elif self.picture:
            self.picture.play(painter)

    # 缓存图片
    #----------------------------------------------------------------------
    def createPic(self,xmin,xmax):
        picture = QtGui.QPicture()
        p = QtGui.QPainter(picture)
        [pic.play(p) for pic in self.pictures[xmin:xmax]]
        p.end()
        return picture

    # 定义边界
    #----------------------------------------------------------------------
    def boundingRect(self):
        return QtCore.QRectF(0,self.low,len(self.pictures),(self.high-self.low))

K线展示的核心功能使用pyqtgraph实现,使用了ItemUsesExtendedStyleOption,并且根据逻辑判断界面需要更新的部分。

相比pyqtgraph示例里面的重画整个图形,界面更新指数级加快了!!

加载1个月的1分钟K线,界面仍能较流畅显示。

载入K线数据接口,使用pandas.DataFrame格式,支持全部载入和一根根载入

#----------------------------------------------------------------------
    def onBar(self, bar, nWindow = 20):
        """
        新增K线数据,K线播放模式
        nWindow : 最大数据窗口
        """

    #----------------------------------------------------------------------
    def loadData(self, datas):
        """
        载入pandas.DataFrame数据
        datas : 数据格式,cols : datetime, open, close, low, high, volume, openInterest
        """

展示策略的技术指标和开平仓标记,开平仓标记通过listSig传入

#----------------------------------------------------------------------
    def addSig(self,sig):
        """新增信号图"""
        if sig in self.sigPlots:
            self.pwKL.removeItem(self.sigPlots[sig])
        self.sigPlots[sig] = self.pwKL.plot()
        self.sigColor[sig] = self.allColor[0]
        self.allColor.append(self.allColor.popleft())

    #----------------------------------------------------------------------
    def showSig(self,datas):
        """刷新信号图"""
        for sig in self.sigPlots:
            self.sigData[sig] = datas[sig]
        [self.sigPlots[sig].setData(datas[sig], pen=self.sigColor[sig][0], name=sig)\
         for sig in self.sigPlots]# if sig in datas]

    #----------------------------------------------------------------------
    def plotMark(self):
        """显示开平仓信号"""
        # 检查是否有数据
        if len(self.datas)==0:
            return
        for arrow in self.arrows:
            self.pwKL.removeItem(arrow)
        # 画买卖信号
        for i in range(len(self.listSig)):
            # 无信号
            if self.listSig[i] == 0:
                continue
            # 买信号
            elif self.listSig[i] > 0:
                arrow = pg.ArrowItem(pos=(i, self.datas[i]['low']),  angle=90, brush=(255, 0, 0))
            # 卖信号
            elif self.listSig[i] < 0:
                arrow = pg.ArrowItem(pos=(i, self.datas[i]['high']), angle=-90, brush=(0, 255, 0))
            self.pwKL.addItem(arrow)
            self.arrows.append(arrow)

说好的开源

K线工具 Github

K线工具下载链接

下载代码并安装依赖的Python库后,双击 uiKLine.py 就可以看到K线哦 !

写在最后

感谢vn.py让我入门量化,并且一步步走到目前小有盈利。如果vn.py后续有开发回测结果K线展示界面的计划,我希望能贡献点力量!@用python的交易员

后续还会继续开源上面【回测结果K线展示】图中工具,敬请期待~

因为本人工作是开发交易客户端,推荐下我们团队开发的无限易交易客户端,可以连接国内四大期货,外盘和上海黄金模拟环境,免费使用,账户和密码都是888888。

![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1920' height='1030'></svg>)

交易客户端

标签

Python回测量化研究K线